埋点统计~~从UITableView数据曝光说起

埋点行为背景

金融产品中为了配合好运营 做好产品的营销和推广,往往在产品中加入一些埋点统计。这些统计常见的有产品曝光率 数据的转化率 用户的行为操作,市面上有很多平台做这些数据采集一键式的集成来辅助做好用户行为分析。然鹅这些粗粒度的统计产品往往不能全面很好的辅助互联网公司做好用户采集和建立用户画像模型,因此有条件的公司内部会做自己的用户行为埋点统计,打造一套自己的用户行为分析平台……

数据埋点采集对产品的意义

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)
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容