埋点行为背景
金融产品中为了配合好运营 做好产品的营销和推广,往往在产品中加入一些埋点统计。这些统计常见的有产品曝光率
数据的转化率
用户的行为操作
,市面上有很多平台做这些数据采集一键式的集成来辅助做好用户行为分析。然鹅这些粗粒度的统计产品往往不能全面很好的辅助互联网公司做好用户采集和建立用户画像模型,因此有条件的公司内部会做自己的用户行为埋点统计,打造一套自己的用户行为分析平台……
数据埋点采集对产品的意义
UI控件在iOS移动端承载这丰富的信息输出,用户在使用浏览中会留下丰富的数据采集信息
A用户会慢慢的逐条浏览呈现的产品
B用户往往会快速滑动进行浏览
C用户会快速的翻页进行浏览
上面的三种用户基本涵盖了这个UII所能涵盖的大部分用户行为,伴随着产生的用户行做分析采集即可分辨出用户的兴趣点
产品的曝光度
用户对商品的兴趣
等一系列
UITableView数据条目曝光
UITableView中用户浏览到的商品条目
怎么定义这个浏览到的产品呢?目前可以这么定义---当页面滑动结束或首次加载完成时呈现给用户的所有商品均算是曝光
UITableView中用户浏览中略过的商品条目
这个就有意思了,用户在快速浏览中可能会快速滑动翻页,更有甚者直接快速滑动之后让列表自己滑动,UI在阻尼情况下会慢慢自动静止,那么这被用户快速划过忽略的就是商品条目就是产品中没有曝光的数据。
逻辑实现
。当前展示的条目比较好计算,直接使用系统API即可
func getAllFullVisiableCelles() -> [UITableViewCell] {
return self.visibleCells.filter { (cell: UITableViewCell) -> Bool in
return isCellFullinScreen(cell)
}
}
。对于用户滑动过程中忽略掉的条目比较复杂--上滑
下滑
手指按着屏幕滑动
在讲解之前我们先要处理一个问题:怎么判断屏幕滑动的方向
在系统中我们根据给定的Pan手势的移动来判断
extension UIScrollView {
enum ScrollDirection {
case up
case down
case left
case right
case idle
}
var direction: ScrollDirection {
var dir:ScrollDirection = .idle
let point = panGestureRecognizer.translation(in: superview)
if point.y > 0 {
dir = .down
} else {
dir = .up
}
return dir
}
}
上滑上滑可分为两种情况考虑
1按照屏幕上滑直到松手
对于这种情况来说是不会出现略过的情况
2 点着屏幕迅速的向上划一下
对对于这中情况来说,屏幕会在惯性作用下自由上滑一段后减速,然后停止。而这个简单的过程也可分为两种
1 滑动一小段就停止,那么所有展示的商品对于用户都是可见的,也就不需要采集这部分数据
2 滑动超过了一个屏幕甚至更多,那么这时候就会有部分商品是被快速划过,用户没有看到。也就是这些没被看到的数据就是需要统计的数据
我们来分析一下这个结果是如何获取的
1 记录最底部的位置(需要完全展示)v0
2 滑动之后自由滑动需要超过一屏幕
3 结束滑动记录顶部位置v99+
那么
v0~v99+
之间的即是快速滑动过程中没被用户认真浏览的数据,也就这些商品的信息没有被用户获取到。无论是对于公司还是投放人员来说都是无用的,因为用户根本没有获取到投放的信息…也就无从说起转化率--投放人只是投放根本获取不到收益
实践分析
1 记录临界点
临界点结构
struct TrackerInfo {
var topIndexPath:IndexPath
var bottomIndexPath: IndexPath
var direction:UIScrollView.ScrollDirection = .idle
var contentOffset: CGPoint = .zero
var currentDate: Date = Date()
}
1.1 采集起点
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
print("开始拖拽\(scrollView.direction)")
trackerInfo.trackerWillBeginDragging(scrollView)
}
1.2 采集终点
手指在屏幕上滑来滑去
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
print("拖拽结束\(scrollView.direction)")
trackerInfo.trackerDidEndDragging(scrollView, willDecelerate: decelerate)
}
松手后自由滑动后停止
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
print("减速结束\(scrollView.direction)")
trackerInfo.trackeDidEndDecelerating(scrollView)
}
1.3 计算
有了临界点我们可以进行计算了
计算起始位置
func trackerWillBeginDragging(_ scrollView: UIScrollView) {
startScrollTime = Date()
self.endInfo = nil
if let tableView = scrollView as? UITableView {
let topIndexPath = tableView.getTopCellIndexPathOnScreen(false) ?? IndexPath.zero
let bottomIndxPath = tableView.getBottomCellIndexPathOnScreen(false) ?? IndexPath.zero
let trackerInfo = TrackerInfo(topIndexPath: topIndexPath,
bottomIndexPath: bottomIndxPath,
direction:scrollView.direction,
contentOffset:scrollView.contentOffset)
self.startInfo = trackerInfo
}
}
func trackerDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if let tableView = scrollView as? UITableView {
let topIndexPath = tableView.getTopCellIndexPathOnScreen(false) ?? IndexPath.zero
let bottomIndxPath = tableView.getBottomCellIndexPathOnScreen(false) ?? IndexPath.zero
let trackerInfo = TrackerInfo(topIndexPath: topIndexPath,
bottomIndexPath: bottomIndxPath,
direction: scrollView.direction,
contentOffset:scrollView.contentOffset)
self.endInfo = trackerInfo
if decelerate == false {
self.delegate?.logItemObserve(tableView.getAllFullVisiableCells())
}
logSlideSkipObserve(tableView)
}
}
计算终点位置
func trackerWillBeginDecelerating(_ scrollView: UIScrollView) {
if let tableView = scrollView as? UITableView {
let topIndexPath = tableView.getTopCellIndexPathOnScreen(false) ?? IndexPath.zero
let bottomIndxPath = tableView.getBottomCellIndexPathOnScreen(false) ?? IndexPath.zero
let trackerInfo = TrackerInfo(topIndexPath: topIndexPath,
bottomIndexPath: bottomIndxPath,
direction: scrollView.direction,
contentOffset:scrollView.contentOffset)
self.endInfo = trackerInfo
}
}
func trackeDidEndDecelerating(_ scrollView: UIScrollView) {
if let tableView = scrollView as? UITableView {
let topIndexPath = tableView.getTopCellIndexPathOnScreen(false) ?? IndexPath.zero
let bottomIndxPath = tableView.getBottomCellIndexPathOnScreen(false) ?? IndexPath.zero
let trackerInfo = TrackerInfo(topIndexPath: topIndexPath,
bottomIndexPath: bottomIndxPath,
direction: scrollView.direction,
contentOffset:scrollView.contentOffset)
self.endInfo = trackerInfo
logSlideSkipObserve(tableView)
self.delegate?.logItemObserve(tableView.getAllFullVisiableCells())
}
}
由于可能会出现用户在上下滑动之后才让屏幕自由放飞,因此我们需要根据最终的数据来判断最终的滑动方向
var direction: UIScrollView.ScrollDirection {
print("\(startInfo?.contentOffset ?? .zero) == \(endInfo?.contentOffset ?? .zero)")
if endInfo == nil || startInfo == nil {
return .idle
}
if startInfo?.contentOffset.y ?? 0 < endInfo?.contentOffset.y ?? 0 {
return .up
}
return .down
最后到我们的计算
func logSlideSkipObserve(_ tableView: UITableView) {
let finalDirection = direction
if finalDirection == .idle {
return
}
var startIndex, endIndex: IndexPath
if finalDirection == .down {
startIndex = endInfo?.bottomIndexPath ?? .zero
endIndex = startInfo?.topIndexPath ?? .zero
} else {
startIndex = startInfo?.bottomIndexPath ?? .zero
endIndex = endInfo?.topIndexPath ?? .zero
}
if startIndex.section < endIndex.section || (startIndex.section == endIndex.section && startIndex.row < endIndex.row) {
let cellIndexPaths: [IndexPath] = tableView.indexPaths(between: startIndex, right: endIndex)
if cellIndexPaths.isEmpty {
return
}
delegate?.logSlideSkipObserve(cellIndexPaths, scrollDirection: finalDirection, start: startInfo?.currentDate, end: endInfo?.currentDate)
}
}