列表曝光埋点框架
埋点时机
我这里 在滚动停止埋点(这种停止滚动时候埋点暂且称为无效点),
开始拖动埋点(这时候通过判断拖动之前停留的时间长短来确定是否埋点)
框架hook 了三个 系统方法
-
(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
-
(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
-
(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
核心代码
/// tableView滑动停止
/// @param options hook类型
+ (void)track_tableViewImpression_scrollViewDidEndDecelerating:(AspectOptions)options
presenter:(NSObject *)presenter;
{
NSError *error = nil;
[presenter aspect_hookSelector:@selector(scrollViewDidEndDecelerating:) withOptions:options usingBlock:^(id<AspectInfo> data){
UITableView *tableView = [data arguments].firstObject;
NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
NSTimeInterval time = [date timeIntervalSince1970]*1000;//
tableView.preScrollEndTime = time;
NSMutableArray *cellsArray = [self unEffectiveCellArrayWithTableView:tableView];
[helper unEffectiveImpressionWithTableViewCellArray:cellsArray];
} error:&error];
}
///拖动停止, 这里需要注意,要在不减速的情况先
+ (void)track_tableViewImpression_scrollViewDidEndDragging:(AspectOptions)options
presenter:(NSObject *)presenter;
{
NSError *error = nil;
[(NSObject *)presenter aspect_hookSelector:@selector(scrollViewDidEndDragging:willDecelerate:) withOptions:options usingBlock:^(id<AspectInfo> data){
UITableView *tableView = [data arguments].firstObject;
BOOL decelerate = [[data arguments].lastObject boolValue];
if (!decelerate) {
NSMutableArray *cellsArray = [self unEffectiveCellArrayWithTableView:tableView];
NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
NSTimeInterval time = [date timeIntervalSince1970]*1000;//
tableView.preScrollEndTime = time;
[helper unEffectiveImpressionWithTableViewCellArray:cellsArray];
}
} error:&error];
}
///开始拖动, 进行埋有效点
+ (void)track_tableViewImpression_scrollViewBeginDragging:(AspectOptions)options
delegate:(NSObject *)delegate
{
NSError *error = nil;
[(NSObject *)delegate aspect_hookSelector:@selector(scrollViewWillBeginDragging:) withOptions:options usingBlock:^(id<AspectInfo> data){
UITableView *tableView = [data arguments].firstObject;
NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
NSTimeInterval time = [date timeIntervalSince1970]*1000;//
if (time - tableView.preScrollEndTime > effectiveTimeThreshhold) {
///时间差大于阈值,才进行有效曝光埋点
[helper effectiveImpressionWithTableViewCellArray:tableView.preEffectiveCellsArray];
}
} error:&error];
}
获取当前展示cell
/*注意:我们是通过CGRectContainsRect(tableView.bounds,
来判断cell是否完全在tableView中的*/
+ (NSMutableArray <UITableViewCell *> *)cellArrayWithTableView:(UITableView *)tableView
{
NSMutableArray *cellsArray = [NSMutableArray new];
for (UITableViewCell *cell in tableView.visibleCells) {
BOOL isRealShow = CGRectContainsRect(tableView.bounds, cell.frame);
if (!isRealShow) {
continue;
}
[cellsArray addObject:cell];
}
return cellsArray;
}
数据去重
我这里给tableView 绑定了 preEndScrollCellsArray属性,用来记录上次停止的时候展示的cell, 和这次停止的对比,做数据去重
/*
记录上次滚动停止的时候展示的cell indexpath数组,和现在停止时候
cell的indexPath数组对比,用来做数据去重
*/
@property (nonatomic, strong) NSArray *preEndScrollCellsArray;
/*
记录上次开始滚动时候有效曝光数组,和现在开始拖动cell
的indexPath数组对比,用来做数据去重
*/
@property (nonatomic, strong) NSArray *preEffectiveCellsArray;
核心代码
+ (NSMutableArray <NSIndexPath *> *)unEffectiveCellArrayWithTableView:(UITableView *)tableView
{
NSMutableArray *cellsArray = [self cellArrayWithTableView:tableView];
NSMutableArray *preEndScrollIndexPathsArray = [NSMutableArray array];
NSMutableArray *preEffectiveIndexPathsArray = [NSMutableArray array];
[cellsArray enumerateObjectsUsingBlock:^(UITableViewCell * obj, NSUInteger idx, BOOL * _Nonnull stop) {
/*数据去重, 这里使用indexpath 对比进行去重,没有直接使用cell, 因为
cell涉及到重用,直接用cell来比较不准确
*/
NSIndexPath *cellIndexPath = [tableView indexPathForCell:obj];
__block BOOL hasBeenInPreIndexPathArray = NO;
[tableView.preEndScrollCellsArray enumerateObjectsUsingBlock:^(NSIndexPath *preIndex, NSUInteger idx, BOOL * _Nonnull stop) {
if (cellIndexPath.row == preIndex.row &&
cellIndexPath.section == preIndex.section) {
hasBeenInPreIndexPathArray = YES;
}
}];
if (!hasBeenInPreIndexPathArray) {
[preEffectiveIndexPathsArray addObject:cellIndexPath];
}
[preEndScrollIndexPathsArray addObject:cellIndexPath];
}];
tableView.preEffectiveCellsArray = preEffectiveIndexPathsArray;
tableView.preEndScrollCellsArray = preEndScrollIndexPathsArray;
return preEffectiveIndexPathsArray;
}
使用方法
pod ‘LBTrackManager’
创建埋点helper继承于 LBTrackHelper
@interface LBSubTrackHelper : LBTrackHelper
@end
重写 - (void)unEffectiveImpressionWithTableView:(UITableView *)tableView
cellIndexPathsArray:(NSArray<NSIndexPath *> *)array 和- (void)effectiveImpressionWithTableView:(UITableView *)tableView
cellIndexPathsArray:(NSArray<NSIndexPath *> *)array两个方法 实现埋点,这里我只做一个示例,打印展示的cell
@implementation LBSubTrackHelper
- (void)unEffectiveImpressionWithTableView:(UITableView *)tableView
cellIndexPathsArray:(NSArray<NSIndexPath *> *)array
{
[array enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"展示的内容内容%ld",obj.row);
}];
}
- (void)effectiveImpressionWithTableView:(UITableView *)tableView
cellIndexPathsArray:(NSArray<NSIndexPath *> *)array
{
[array enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"有效曝光有效曝光展示的内容内容%ld",obj.row);
}];
}
@end
app启动的时候 配置helper
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
LBSubTrackHelper *helper = [[LBSubTrackHelper alloc] init];
[LBTrackManager configTrackHelper:helper];
// Override point for customization after application launch.
return YES;
}
在某个需要埋点的页面注册tableView即可
[LBTrackManager trackRegisterTableView:self.tableView tableViewDelegate:self];
如下图,是我测试的效果,准确的获取了滚动停止的cell
接着再次滚动(上次是曝光到第12条),查看展示内容,已经去重