iOS 列表cell曝光埋點

什么是曝光埋點,簡單的說就是展示在屏幕上了,然后往服務(wù)端上傳一個埋點。那列表cell的曝光埋點就是cell進(jìn)入屏幕里面了。具體需求看下面
一,需求:
a.cell出現(xiàn)70%算出現(xiàn)在屏幕,當(dāng)然這個可以改。
b.cell在屏幕上停留一秒算是真的曝光(其實這個1s我并沒有實現(xiàn))

二,心路歷程:
拿到一個新的需求怎么辦當(dāng)然是百度一下,結(jié)果網(wǎng)上都是列表停止滑動才開始算的。我們需求要求慢慢滑動cell在屏幕里超過1s也算。好家伙我直接沒辦法了。網(wǎng)上都是停止滑動這個時間點來做一個上報的時機(jī),現(xiàn)在這個時機(jī)沒有了怎么辦。想了半天最后決定用一秒一次的定時器來實現(xiàn)。這樣就導(dǎo)致我的停留一秒并不是很準(zhǔn)確。(什么?為什么不縮短定時器間隔時間?太快了受不了~)

三,方案:
1,啟動一個1s一次的定時器
2,每次脈沖事件時獲取屏幕中的cell列表
3,用獲取的列表和上一次的對比,這次有的上一次沒有的是新增的,這次有的上一次也有的是在屏幕中停留的,這次沒有的上一次有的是離開屏幕的
4,保存新增的; 給屏幕中停留的記個時; 離開屏幕的計算一下停留時間是否大于1s,大于的話上報埋點。

四,具體代碼

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
@protocol KFZCollectionViewExposureDelegate;
@interface KFZCollectionViewExposure : NSObject
/** 停止處理 (一個頁面多個列表切換需求隱藏的暫時不記錄) */
@property (nonatomic, assign) BOOL pause;
/** 代理 */
@property (nonatomic, weak) id<KFZCollectionViewExposureDelegate> delegate;
-(instancetype)initWithCollectionView:(UICollectionView *)collectionView;

@end

@protocol KFZCollectionViewExposureDelegate <NSObject>
//需要上報埋點
-(void)needReportIndexPath:(NSIndexPath *)index exposure:(KFZCollectionViewExposure *)exposure;

@end
#import "KFZCollectionViewExposure.h"
#import "TimerNotifier.h"
#import "KFZExposureIndexPath.h"

// 露出曝光 百分比
CGFloat const ExposurePercentage = 0.8;

@interface KFZCollectionViewExposure ()<TimerUserInterface>
/** 監(jiān)聽的列表 */
@property (nonatomic, strong) UICollectionView * collectionView;
/** 緩存當(dāng)前屏幕中顯示的indexPath */
@property (nonatomic, strong) NSMutableArray<KFZExposureIndexPath *> * showIndexPaths;
/** 代理方法是否實現(xiàn) */
@property (nonatomic, assign) BOOL needReportSelector;
@end

@implementation KFZCollectionViewExposure

-(instancetype)initWithCollectionView:(UICollectionView *)collectionView{
    self = [super init];
    if(self){
        _collectionView = collectionView;
        
        WS(weakSelf);
        [[TimerNotifier standard] registUser:weakSelf];
        weakSelf.allSeconds = -1;
        [TimerNotifier standard].timeInterval = 1;
    }
    return self;
}

-(void)timeDown{
    if(_pause){
        return;
    }
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSArray * curShowIndexPaths = [self getCalculateExposureIndexPaths];
        NSMutableArray * tmp = [self.showIndexPaths mutableCopy];
        NSMutableArray * newList = [[NSMutableArray alloc]init];
        for (NSIndexPath *indexPath in curShowIndexPaths) {
            BOOL find = NO;
            for (KFZExposureIndexPath * oldIndexPath in tmp) {
                if([oldIndexPath.indexPath isEqual:indexPath]){
                    find = YES;
                    oldIndexPath.time += 1;
                    [tmp removeObject:oldIndexPath];
                    break;
                }
            }
            if(!find){
                KFZExposureIndexPath * newIndexPath = [[KFZExposureIndexPath alloc]init];
                newIndexPath.indexPath = indexPath;
                newIndexPath.time = 0;
                [newList addObject:newIndexPath];
            }
        }
        [self.showIndexPaths removeObjectsInArray:tmp];
        [self.showIndexPaths addObjectsFromArray:newList];
        for (KFZExposureIndexPath * indexPath in tmp){
            if(indexPath.time >= 1){
                if(self.needReportSelector){
                    [self.delegate needReportIndexPath:indexPath.indexPath exposure:self];
                }
            }
        }
    });
}


#pragma mark - 重新計算當(dāng)前區(qū)域曝光的IndexPath
- (NSArray<NSIndexPath *> *)getCalculateExposureIndexPaths {
    __block NSMutableArray * array = [NSMutableArray array];
    NSArray<NSIndexPath *> * indexPathsForVisibleRows = self.collectionView.indexPathsForVisibleItems;
    
    [indexPathsForVisibleRows enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([self calculateExposureForIndexPath:obj]) {
            [array addObject:obj];
        }
    }];
    return array;
}

//計算是否顯示是否超過設(shè)置的百分比ExposurePercentage
- (BOOL)calculateExposureForIndexPath:(NSIndexPath *)indexPath {
    CGRect previousCellRect = [self.collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath].frame;
    
    UIWindow * window = [self lastWindow];
    
    CGRect convertRect = [self.collectionView convertRect:previousCellRect toView:window];
    
    CGRect tabRect = CGRectIntersection([self.collectionView.superview convertRect:self.collectionView.frame toView:window], window.bounds);
    
    UICollectionViewFlowLayout * layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
    BOOL isFlowLayout = [layout isKindOfClass:[UICollectionViewFlowLayout class]];
    if (!isFlowLayout || (isFlowLayout && layout.scrollDirection == UICollectionViewScrollDirectionVertical)) {
        CGFloat currentTop = CGRectGetMinY(convertRect) - CGRectGetMinY(tabRect);
        if (currentTop < 0) {
            CGFloat percentage = (convertRect.size.height + currentTop) / convertRect.size.height;
            if (percentage >= ExposurePercentage) {
                return YES;
            }
        } else {
            CGFloat currentBottom = CGRectGetMaxY(tabRect) - CGRectGetMaxY(convertRect);
            if (currentBottom < 0) {
                CGFloat percentage = (convertRect.size.height + currentBottom) / convertRect.size.height;
                if (percentage >= ExposurePercentage) {
                    return YES;
                }
            } else {
                return YES;
            }
        }
    } else {
        CGFloat currentLeft = CGRectGetMinX(convertRect) - CGRectGetMinX(tabRect);
        if (currentLeft < 0) {
            CGFloat percentage = (convertRect.size.width + currentLeft) / convertRect.size.width;
            if (percentage >= ExposurePercentage) {
                return YES;
            }
        } else {
            CGFloat currentRight = CGRectGetMaxX(tabRect) - CGRectGetMaxX(convertRect);
            if (currentRight < 0) {
                CGFloat percentage = (convertRect.size.width + currentRight) / convertRect.size.width;
                if (percentage >= ExposurePercentage) {
                    return YES;
                }
            } else {
                return YES;
            }
        }
    }
    return NO;
}

- (UIWindow *)lastWindow{
    NSArray *windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in [windows reverseObjectEnumerator]) {
        if ([window isKindOfClass:[UIWindow class]] && CGRectEqualToRect(window.bounds, [UIScreen mainScreen].bounds)) {
            return window;
        }
    }
    return windows.lastObject;
}

#pragma mark - init

-(void)setDelegate:(id<KFZCollectionViewExposureDelegate>)delegate{
    _delegate = delegate;
    _needReportSelector = [_delegate respondsToSelector:@selector(needReportIndexPath:exposure:)];
}

- (NSMutableArray<KFZExposureIndexPath *> *)showIndexPaths{
    if(nil == _showIndexPaths){
        _showIndexPaths = [[NSMutableArray alloc]init];
    }
    return _showIndexPaths;
}

#pragma mark - timer

@synthesize allSeconds;
-(void)receivedTimerUpData:(NSString *)timeString{
    [self timeDown];
}

@end

其中TimerNotifier是我之前寫的定時器http://www.itdecent.cn/p/9d6e67ffbbd8。KFZExposureIndexPath是個小的保存時間和index的類。代碼如下

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface KFZExposureIndexPath : NSObject
/** 位置 */
@property (nonatomic, strong) NSIndexPath * indexPath;
/** 曝光時間 */
@property (nonatomic, assign) NSInteger time;
@end

NS_ASSUME_NONNULL_END

五,具體使用:

#import "KFZCollectionViewExposure.h"

<KFZCollectionViewExposureDelegate>

@property (nonatomic, strong) KFZCollectionViewExposure * exposureTool;

_exposureTool = [[KFZCollectionViewExposure alloc]initWithCollectionView:_collectionView];
_exposureTool.delegate = self;

#pragma mark - KFZCollectionViewExposureDelegate
//需要上報埋點
-(void)needReportIndexPath:(NSIndexPath *)index exposure:(KFZCollectionViewExposure *)exposure{
    
}

OK,打完收工~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容