产品或者运营会要求统计在UITableView\UICollectionView\UIScrollView中,每个卡片的曝光情况。一般的比较简单的实现方式就是在
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
(cell as! TableViewCell).updateUI(value: indexPath.row)
//在这里进行曝光统计
return cell
}
但是这样统计就有几个问题。
- 提前曝光。这个时候其实卡片还未展示到屏幕上,但是代码已经显示曝光了。
- 无法确定卡片是否完全曝光。有些产品需求是需要统计卡片完全曝光的情况,那么这种实现方式是无法满足的。
所以不能在创建cell的时候曝光,而应该是UI界面改动的时候实时统计当前Cell的曝光情况,基于这个思路,设计实现了一套方案。
方案原理
当UIScrollView(UITableView/UICollectionView)滚动/UI变化时,计算每个Cell和UIScrollView的可见矩形区域是否有交集来判断是否曝光。
方案难点
- 需要考虑所有导致UI变化的情况。包括contentoffset变化、UIScrollView的frame变换、transform变化、父布局变化、数据变化等等情况。
- 已经曝光过的卡片,滑出屏幕后,再回到屏幕时,需要再次曝光。
- UIScrollView的子类有多种,如何通过一套机制来满足绝大多数情况。
- 接入的复杂度要低。要将接入者的工作量降到最低。
- 性能要求。不能对性能要明显影响。
实现成果
- 满足的场景。
1、内置支持UITableview、UICollectionview、UIScrollview、UIScrollviewCollectionKit的CollectView。
2、另一种比较特殊的场景,某个UIView作为一个容器,在UIScrollview中滚动,需要曝光统计该UIView容器中的子View.
3、支持嵌套UIScrollview的曝光,譬如UIScrollview A中包含一个SubView UIScrollview B,需要统计B中的子View曝光情况。
4、一个大的UIScrollView包含几个子的UIScrollView,需要对子的UIScrollView进行曝光统计。
5、支持跳转到别的UIViewController后,回到当前页面,需要重新曝光的要求。 - 接入简单。
下面以UITableview为例。继承CellExposureLogUITableView,并根据需要override对应的ouput回调函数即可。
如下方式适用于,tableview的数据定义在CustomUITableView内部的情况,这样可以直接回调输出当前曝光的卡片。
class CustomUITableView: CellExposureLogUITableView<String> {
var testData = [Int]()
<!-- 这个一般返回数据的唯一id,跟选择的泛型类型匹配即可 -->
override func indexMapToKey(index: CellExposureLogUITableView<String>.IndexType) -> String {
return String(testData[index.row])
}
override func outputCompleteVisibleItems(items: Set<KeyIndexCompose<String, CellExposureLogUITableView<String>.IndexType>>) {
super.outputCompleteVisibleItems(items: items)
}
override func outputPartVisibleItems(items: Set<KeyIndexCompose<String, CellExposureLogUITableView<String>.IndexType>>) {
super.outputPartVisibleItems(items: items)
}
override func outputCustomExposureRatioItems(items: Set<KeyIndexCompose<String, CellExposureLogUITableView<String>.IndexType>>) {
super.outputCustomExposureRatioItems(items: items)
}
}
具体的使用方式,可以查看项目中代码中的Example.
https://github.com/dongzhixuanyuan/ListViewCellExposureLog