网易HubbleData无埋点SDK在iOS端的筹划以及实现

3.3.2 UIButton

UIControl中应用最多尽广大的凡UIButton,因此对UIButton的集很关键。在行使UIButton的当儿可以任意的装其title等属性来代表事情逻辑的差状态。这里可以选一个简易的事例:基本app的报到页面,在用户称和密码都非输入时、都输入时和登录中各个状态,登录按钮的title、titleColor等性可能还是差的,即每一样种植button的样式且表示在雷同种样式,但是得的EventID是同等之。针对是种植情形,HubbleData会参加title、titleColor作为属性值,以方便后台进行更的剖析。

当按钮的简单栽状态只有是少数种植不同的背景图片时,比如微博要微信的点赞等,其实是移了平栽背景图片,针对对这种场面处理,HubbleData则会沾图片的imageName作为里一个属性。

(1) type 为UIControl采集的事件类型,这里设置为buttonEvent;
(2) page 为当前页面的名称,用于前端显示用;
(3) title 为当前按钮的title;
(4) titleColor 为当前title的color,会转换成字符串的形式,rgba(r, g, b, alpha);
(5) imageName 为当前按钮的背景图片的name;
(6) frame 为UIButton的frame,用于分析同类元素,会转换成字符串的形式,rect(x, y, width, height);

可看看,HubbleData还搜集了该view的frame信息,主要是用来分析同类元素用的,下图让有一个简便的示范:

button

此时此刻出六只就关注的制品,当思统计用户所有点赞的轩然大波频仍,由于每个点赞的按钮都处于一个UITableViewCell中,在头里介绍的得到层级唯一路径UITableViewCell时的与众不同处理,由于每个按钮所当的cell的row不同,所以取的每个按钮的风波之唯一EventID都是不同之,这样后端在分析的上,无法归类同类元素。当HubbleData给来frame时,后端可以因frame归类出同一好像按钮的风波,具体的分类策略这里不再介绍。

– (id)initWithFrame:(CGRect)frame{

    self = [super initWithFrame:frame];

    if(self){

          self.backgroundColor = [UIColor clearColor];

         WS(weakSelf);

         _scrollContentView = [[UIScrollView alloc]
initWithFrame:self.bounds];

         _scrollContentView.backgroundColor = [UIColor
clearColor];

         [self addSubview:_scrollContentView];

         [_scrollContentView
mas_makeConstraints:^(MASConstraintMaker *make) {

              //重点来了,scrollview 要求跟当前 view 一边大

              make.edges.equalTo(weakSelf);

         }];

         __weak UIScrollView *weakScroll = _scrollContentView;

         _pieView = [[UIView alloc] initWithFrame:self.bounds];

         _pieView.backgroundColor = [UIColor clearColor];

         [_scrollContentView addSubview:_pieView];

         [_pieView mas_makeConstraints:^(MASConstraintMaker *make)
{

              //饼图中心点 X 值要跟 scrollview 一样,头部要在
scrollview 上留下有kMarginY大小的偏离

              make.centerX.equalTo(weakScroll.centerX);

             
make.top.equalTo(weakScroll.top).with.offset(kMarginY);

         }];

         __weak UIView *weakPie = _pieView;

         _infoView = [[UIView alloc]
initWithFrame:self.bounds];

         _infoView.backgroundColor = [UIColor whiteColor];

         [_scrollContentView addSubview:_infoView];

         [_infoView mas_makeConstraints:^(MASConstraintMaker
*make) {

              //白色信息图的中心点 X 值 Y 值都使跟饼图一样

              make.centerX.equalTo(weakPie.centerX);

              make.centerY.equalTo(weakPie.centerY);

         }];

         _descriptionView = [[UIView alloc]
initWithFrame:self.bounds];

         _descriptionView.backgroundColor = [UIColor clearColor];

         [_scrollContentView addSubview:_descriptionView];

         [_descriptionView mas_makeConstraints:^(MASConstraintMaker
*make) {

              //颜色说明 view 的宽度要同 scrollview
一样,头部要当饼图底部距离kPieDesSpace的岗位

              make.width.equalTo(weakScroll);

             
make.top.equalTo(weakPie.bottom).with.offset(kPieDesSpace);

         }];

   }

    return self;

}

-(void)setFrame:(CGRect)frame{

    [super setFrame:frame];

    self.pieRadius = MIN(self.bounds.size.width/2,
self.bounds.size.height/2) – kMarginX*2;

    _animationArr = [NSMutableArray array];

    self.textRadius = _pieRadius – (_pieRadius-kInfoRadius)/2;

    //注意这里以的是
update,因为自前曾针对他们设置过约,我只是想补充加新的律,如果还是用make
的说话之前的具备约束都见面失效,如果就此 remake
的讲话虽是将之前的应和约束替换掉。因为我现已亮 frame
了,现在己可以将他们的分寸进行约束了

    [_pieView updateConstraints:^(MASConstraintMaker *make) {

         make.size.mas_equalTo(CGSizeMake(_pieRadius*2,
_pieRadius*2));

    }];

    [_infoView updateConstraints:^(MASConstraintMaker *make) {

         make.size.mas_equalTo(CGSizeMake(kInfoRadius *2,
kInfoRadius *2));

    }];

}

– (void)setPieRadius:(CGFloat)pieRadius{

    _pieRadius = pieRadius;

    [_pieView.layer setCornerRadius:_pieRadius];

    [_infoView.layer setCornerRadius:kInfoRadius];

}

>三、无埋点的搜集的贯彻

前面的代码是这般的:

2.1.2 几种特殊状况的处理

2.1.1重点谈的是一些惯常view的层级结构的path构造方式,但是出一部分新鲜情况需要特地之设想处理:

  • UITableViewCell

出于UITableViewCell具有可复用的体制,当一个页面中于时时刻刻滚动的时候,cell在时时刻刻的复用,如果还利用2.1.1着介绍的方来获得index索引值话,那么会唤起上上下下页面无埋点数据搜集的混杂。

当得当前UITableViewCell的index时,可以使用indexPath参数进行替换,这个参数可精确的获得section和row的值,唯一的应和每一个cell。唯一层级路径的款型得以起定义配置,HubbleData的安装方式也:类名+(section:
row:),下面为闹一个演示:

MyTableViewCell(section:0 row:7)
  • UICollectionViewCell

UICollectionViewCell的path生成原理同UITableViewCell,HubbleData的装方法啊:类名+(section:item:),下面为闹一个演示:

MyCollectionViewCell(section:0 item:7)
  • UIControl

骨子里UIButton也终究一栽常见view的平等种植,大多数情下,使用上述的层级结构path以及页面类名的成会唯一的确定当前UIControl的唯一标识符,但是来同等种独特之景象,当作为UINavigationItem时会并发异常状况,下面的所让闹的有限独例子。

bar1

bar2

当点击第一个NavigationBar的右侧的按钮时,得到的层级路径也:

...UIViewControllerWrapperView(0)_UIView(0)_UILayoutContainerView(0)_UINavigationBar(0)_UIButton(1)

浅析会,左侧的设置按钮的目录为0,所以右侧的按钮索引为1。同时获之手上页面也:UINavigationController。

当点击第二个页面的与一个品类的按钮时,即一律标明出数字7的item时,此时抱的层级路径也:

...UIViewControllerWrapperView(0)_UIView(0)_UILayoutContainerView(0)_UINavigationBar(0)_UIButton(2)

好窥见此时之按钮的目录变成了2,已经今非昔比让上述第一个NavigationBar的跟一个按钮的层级路径了,经过分析,索引值为1底按钮是最为右边的表的十分item,经过验证可以获其层级路径:

...UIViewControllerWrapperView(0)_UIView(0)_UILayoutContainerView(0)_UINavigationBar(0)_UIButton(1)

获的页面也:UINavigationController。

其实这种页面很常见,由于页面的切换,NavigationBar上的有些按钮的职位也许顺序会打乱,导致与一个效益的NavigationItem已经无法确定标识唯一,即使是赢得了时按钮所于的页面也无法区分,因为落之且是UINavigationController。从上面的辨析好看来,这种情形甚至会促成严重混乱的数目收集。

实际上仔细分析一下,如果条分缕析得出该UIControl是当UINavigationBar上,则任需安装其对应的index值,即上述的保有navigationItem的层级结构路径都也:

...UIViewControllerWrapperView(0)_UIView(0)_UILayoutContainerView(0)_UINavigationBar(0)_UIButton

就是都无举行区分。

HubbleData采用多一种植新的属性来区别各个item,其实深显眼得关押下,这个item的推行之action肯定是差的,所以取其action属性来区别,最终之区别形式如下:

path(...UIViewControllerWrapperView(0)_UIView(0)_UILayoutContainerView(0)_UINavigationBar(0)_UIButton)&actions(button1Click:)
path(...UIViewControllerWrapperView(0)_UIView(0)_UILayoutContainerView(0)_UINavigationBar(0)_UIButton)&actions(button2Click:)

这般,HubbleData就足以准确的别不同的item了,同时落实均等栽力量的item,由于其action相同,所以呢会规范之标识其唯一性。

  • UIAlertController

由不同之UIAlertController在挑选确定、取消当选择时,选取的进行唯一层级路径判定的view需要开展定的拍卖,同时为确保不同之UIAlertController处于同一职位的取舍之埋点EventID不同,这里当结构唯一标志字符串的时段还要在该UIAlertController的message和title信息。3.5小节中见面开展相关无埋点采集的牵线。

  • viewController的嵌套

一般情形下,普通的view只待以一般的层系路径收集index即可,但是当在pageViewController时,如下图所展示分别于来了一个横向滚动(以企业考拉app为例)和纵向滚动(以商店严选app为条例)的app的截图的以身作则:

事实上可以观看,pageViewController会应用至繁app中,所以这看似app在采取过程被之无埋点问题愈加要考虑。

图片 1

(1) 各个子页面的controller不同?

假如pageViewController中的依次子页面不同,虽然持续2.2节HubbleData会加盟页面controller的音信来分别这些不同的子页面,但是或许会见由每个子页面加入的逐一不同,导致每次app进来的上同一个页面的波会沾不同的EventID,举例来说明一下,如齐图1所显示,比如前面四单子页面是ViewController1,
ViewController2, ViewController3,
ViewController4,这类似pageViewController除非设置四独子页面同时预加载出来,那么此时的获取之层级路径为:

ViewController1对应路径为:superview(0)_subControllerView(0) 
ViewController2对应路径为:superview(0)_subControllerView(1)
ViewController3对应路径为:superview(0)_subControllerView(2)
ViewController4对应路径为:superview(0)_subControllerView(3)

只是app基本都不见面预加载出富有页面,对于用户不感兴趣的页面完全没必要一次性全部加载处理,只有当用户选择了拖欠条目时,该对应的子页面才见面加载出来,如果今天用户点击的一一是ViewController1,ViewController3,ViewController4,ViewController2,由于addChildViewController或者addSubView的逐条的改观,那么此时赢得之层级路径为:

ViewController1对应路径为:superview(0)_subControllerView(0) 
ViewController2对应路径为:superview(0)_subControllerView(3)
ViewController3对应路径为:superview(0)_subControllerView(1)
ViewController4对应路径为:superview(0)_subControllerView(2)

可发现,index值变了,层级路径不唯了,那么不论埋点采集的EventID可能会见由于用户选择页面顺序的不等而异,造成埋点数据的繁杂。

HubbleData对于此类页面的处理是,遇到此类页面,即无用index标注,所以会见合并之标识成:

ViewController1对应路径为:superview(0)_subControllerView 
ViewController2对应路径为:superview(0)_subControllerView
ViewController3对应路径为:superview(0)_subControllerView
ViewController4对应路径为:superview(0)_subControllerView

继续可以透过不同的页面的controller的类名获取其殊的唯一标识字符串。

-(void)viewWillAppear:(BOOL)animated{

    [super viewWillAppear:animated];

    _pie.frame = self.view.bounds;

}

一致、埋点简介

第一简单介绍一下 Masonry,Masonry
是平缓缓能够让开发者十分好使iOS的自行布局(AutoLayout)机制。Masonry提供进一步健全、友好之API来顶替直接运用NSLayoutConstraint进行编程,能够如视图布局的进程更逍遥自在。(好吧我承认当时句话是我拷贝过来的,这不是根本)

1.1 三种植埋点的兑现方式简介

埋点的办法分为三类:代码埋点、可视化埋点和无埋点。这里大概的牵线一下老三种埋点方式:

(1)
代码埋点即是于代码的关键部位植入所设募数据的N行代码,需要打起产品本身,深入摸底产品的业务逻辑与项目结构,下面代码模拟显示的便凡点击提交订单的时段HubbleData
SDK代码埋点;

代码埋点示例

(2)
可视化埋点即用可视化交互的主意圈选出所假设募数据的控件,当用户作为发生时,即可收集到相应的埋点数据。相比于前方的代码埋点而言,可视化埋点能够缓解代码埋点代价十分本钱大之题目,但是无法活的自定义埋点属性。

可视化埋点流程

(3)
无埋点也让全埋点,即未待用户主动埋点,可以收集用户拥有的操作行为,同样用可视化圈选,用户能够以到所想征集的埋点数据,能够化解可视化圈选中数量不可回溯的问题。下图让闹了随便埋点多少收集的略流程。

无埋点数据搜集流程

HubbleData
SDK的设计要是代码埋点结合无埋点的数据搜集方式,其中也提到到可视化埋点吃的屏幕序列化及波绑定机制,本文主要介绍一下无埋点的筹划及贯彻。

噢噢太激动了忘记了总结

1.2 无埋点SDK设计详细流程

生图被出HubbleData无埋点SDK在iOS端的计划性实现:

任埋点详细规划流程

从上图可以见见,HubbleData的无埋点是在代码埋点的底子及落实之,所处任埋点的难题也便集中在偏下三单方面:

(1)自动获取埋点的EventID
(2)自动获取埋点的时机
(3)自动获取埋点需采集的属性

本文主要就随即三个点拓展解析,第二局部重要出口一下波唯一ID的确定,第三片主要谈一下无埋点的采的贯彻,主要是各种风波有采集的时机和要采集的性能之配置。

HubbleData
SDK还波及到无数别样职能,包括屏幕序列可视化、代码埋点、精准渠道追踪等,这里不再介绍,后面会陆续分享相关的技艺实现。

实在一开始用autolayout我是不容的,想到如果xb、sb还起法定那么麻烦的话语喔喔还有老看不清楚的象形文字我不怕浑身脑袋疼!但是今发了这样便利的Masonry,就仿照来用用吧,毕竟苹果的屏幕尺寸就初步免一致了,用一个对象的讲话来说,autolayout是方向。不管怎样,了解下连好之

3.3.3 UISwitch

好像于UIButton,只不过这里而收集switchState,即眼前之开关状态,具体的收集属性也:

(1) type 为UIControl采集的事件类型,这里设置为switchEvent;
(2) page 为当前页面的名称,用于前端显示用;
(3) switchState 为switch的开关状态;

先本人已经有几次特别查阅了 AutoLayout
的连锁资料,好吧其实就是一些牛牛在好的博客上的行使教程以及部分心得。一直都想方试试看一下不过一直未曾呀时来执行,这篇稿子主要说的凡本身是何等用
Masonry 移植到原始代码中。

2.1 控件的层级结构path构造

完成。

3.4 UITableView和UICollectionView的无埋点采集

对UITableView和UICollectionView,HubbleData采用的是先hook
UITableView和UICoolectionView的setDelegate:方法,然后找到呼应的delegate,然后又hook
delegate类中之tableView:didSelectRowAtIndexPath:方法与UICollectionView的collectionView:didSelectItemAtIndexPath:方法。这里以UITableView为例:

tableview

EventID按照上述介绍的措施获得,只不过这里要注意的是,获取之并无是UITableView的唯一标识字符串而是对应的点击的cell的绝无仅有标识字符串。采集的properties为:

(1) type 为UITableView采集的事件类型,这里设置为tableViewSelectEvent;
(2) page 为当前页面的名称,用于前端显示用;
(3) section 为点击的cell所在的section;
(4) row 为点击的cell所在的row;

图片 2

四、总结

文章要介绍了HubbleData无埋点SDk在iOS端的宏图以及贯彻,涉及的最主要内容:事件唯一ID的规定和一些无埋点的贯彻,当然在无埋点SDK的规划开被还遇到了丰富多彩的题材。鉴于文章的字数就较丰富,一些问题之缓解及关键技术的实现,比如精准渠道追踪、hook冲突解决、代码埋点的兑现、屏幕序列化以及可视化圈选部分的情,本篇文章不再介绍,将会晤以此起彼伏文章被持续介绍。

由上面的代码,你该能够收看代码原来的逻辑顺序,这样处理代码的目的就通过在初始化这个控件的时光即便根据加的
frame
宽高取最小值作为饼图的直径,进而确定饼图以及白色信息图的frame,但是前提我刚好说了,一切都建立以
frame 是一个让起的固定值的基础及之。我这么说而或会见觉得奇怪,frame
本来就是一个一定好的熨帖的CGRect 啊什么坏啊是勿是神经啊
那么什么情形下而并不知道 frame
到底是小啊?那就是是自家在初始化这个控件的下也就此了
AutoLayout,也就是说我当上一个界面就通过自律来管理之控件的布局,那么自己才需要加以约束规范就是哼并不需要传一个一定的价值来受这控件,像这样:

3.2 UIViewController的无埋点采集

要是采访页面的生命周期,这里HubbleData采用的凡hook
UIViewController的viewWillAppear方法,按照3.1为出的道:

 [DASwizzler swizzleBoolSelector:@selector(viewWillAppear:)
                         onClass:[UIViewController class]
                       withBlock:executeAppearBlock];

当viewWillAppear函数执行时,插入埋点的代码。HubbleData的筹划方式为:

EventID设置也稳的da_screen,即不会见由此EventID来分别各个页面的消息,HubbleData将顺序页面的别信息在了properties中,其中properties的装为:

(1) $screenName 为当前页面的名称;
(2) $screenTitle 为当前页面的title,可为空;

再者HubbleData SDK提供了一个protocol <DAScreenAutoTracker>

即便用户可透过兑现该protocol,HubbleData
SDK会将screenTitle返回的值当页面的称谓,trackProperties返回的性质在对应页面的da_screen事件之属性被,作为用户访问该页面时的事件性质,screenUrl返回的字符串作为页面的Url,用于做有页面中相互跳转的解析等。

而且多了白名单设置,有一对UIViewController的信用户不思采访,可以经过设置白名单的艺术,将部分勿思量征集的UIViewController过滤掉,比如说SFBrowserRemoteViewController,UIInputWindowController等体系自带的片段。

末段会调用trackEvent记录该采访的事件,同上述介绍的代码埋点一样,调用的点子如下:

[[DATracker sharedTracker] trackScreenEvent:@“da_screen” withAttributes:properties];

个中properties即为上述要收集的有的特性。

立即是一个饼图的控件:

老二、事件唯一ID的确定

以促成以可视化圈选的时的波的唯一性,每一个无埋点的轩然大波采访都须来还只有发生一个唯一的标识符来区分不同之风波。不同为代码埋点,用户可以起定义之配备好所急需的EventID,无埋点过程被,需要SDK自己布置各级一个集事件之EventID,通过可视化圈选的操作,筛选产生相应的EventID所对应之数据信息。HubbleData采用的凡结构view唯一标识字符串的措施去唯一的标识这样的一个风波,主要出于view的层级结构path路径、该view的四处页面类名以及view所带来的有本人定位属性等结合,并通过SHA256编码来收获唯一的EventID。

脚将整体系统介绍部分波唯一ID的变过程。

而自己对 Masonry
最初的认识是经过这篇稿子:Masonry介绍及行使实行:快速达标手Autolayout –
 CocoaChina 苹果支付中文站 – 最热之iPhone开发社区 最暖的苹果支付社区
最热之iPad开发社区,并且自己发觉网达到流传的大部分有关
Masonry 的章基本都来源于于此,所以若您对 Masonry
一点叩问都尚未,这首文章确实是那个不利的。

3.3.4 其余UIControl

其他的只是是收集type,page属性,目前无开了多的拍卖。

这就是说现在自己只要举行的就是是受 pie 里面的组成部分控件也 autoLayout,但是我要待拿
view 的 frame 告诉 pie,因为自原先有一个逻辑是要取frame
宽高顶小值来作饼图的直径的,真恼人,那么我们虽活该想艺术告诉 pie
我现在底 frame 是多异常,你又来举行相应调整。好吧,我先行来通知 pie:

3.1 AOP 简介

脚说一下无埋点的求实实现,用到的基本点是AOP(Aspect-Oriented-Programming),面向切面编程,面对的是处理过程中的某个步骤和措施。在运行时,动态的以代码插入到类似的制订措施、指定位置及之编程思想就是面向切面编程。熟悉iOS
Runtime的应当格外懂得,相关的介绍文章吧非常多,这里不再了多之废话。

HubbleData无埋点的实现重大就借助AOP,hook对应类的措施,并当本来实现代码的根基及栽自己定义之埋点的代码,当该类的被hook的函数执行时,就能兑现无埋点数据收集的功能。下面为出HubbleData里面Method
Swizzling的一个简单易行的落实。

Method Swizzling

上述代码只是让出了一个简约的贯彻的逻辑结构,new_swizzledMethod也仅是selector没有参数的状态(除去self和_cmd),真正在埋点的处理过程需要考虑的场面于多。

这就是说现在本人在 pie 里就用变更了:

2.1.1 普通view的层级结构path构造

层级结构path主要是因页面的控件树构造而成为,每个view都起superview与subviews的性,将各个一个view的superview作为培养的父节点,将其subviews作为子节点,这样虽可知拿方方面面app上的富有view组成一株高大之控件树,其中树之顶层是UIWindow,然后是各个一个view节点依次为下展开。下图让有一个简单的控件树的结构图。

空间树结构

脚会详细介绍一下HubbleData的绝无仅有标识路径的结构方式。

不同类

同类

譬如上图1所出示,如果一个view的subviews中还是不同门类的,比如像下图图1所展示之控件树那样,可以唯一标识UILabel和UIButton控件为:

UIView_UILabel
UIView_UIButton

可真正的页面是未见面如优秀中之有着控件都是差品种的,可以说这种极端气象基本未设有,如果要论上述的方法来组织路径的口舌,两个UILabel都见面被标识成UIView_UILabel,这分明无法区分两个控件。因此只有是每个控件节点的途径名称是力不从心唯一标识是控件的,这里HubbleData加入了是控件节点在父视图中的index。比如达图2,可以以有限单UILabel标识为:

UIView(0)_UILabel(0)
UIView(0)_UILabel(1)

这里要父视图是index为0的一个节点,这样就是好了的分别出个别单控件了。

那么余下的题目不怕是每个UIView index索引值的确定。

每个UIView都有subviews属性,每一个子视图都有一个被addsubView的次序,其实若将的是index就是子视图被add的程序,那么该怎么将到这个次呢,在苹果之官方说明文档中,岁UIView的subviews属性,是这样介绍的:

@property(nonatomic, readonly, copy) NSArray *subviews

You can use this property to retrieve the subviews associated with your custom view hierarchies. 
The order of the subviews in the array reflects their visible order on the screen.

不畏各级一个子视图在斯subviews数组中的目录就是HubbleData要以的index。

对繁复的视图形式,如下图所出示,按照上述的层级结构路径构造方法得到的唯一层级路径也:

UIView(0)_UILabel(0)
UIView(0)_UIButton(1)
UIView(0)_UIButton(2)  

混合

自从上述的辨析会,按照上述介绍的计开展view的绝无仅有层级路径标识,对多数的页面来说已经够,但是对片越来越灵活点的页面,由于有的业务要求等原因,开发人员经常会调用removeFromSuperview,
insertSubview:atIndex:, insertSubview:
belowSubview:等函数,都见面大幅度的震慑整个页面的subviews的索引值,比如现在自将达到图所显示之UILabel移动及少单UIButton的后边,那么得的绝无仅有层级路径为:

UIView(0)_UIButton(0)
UIView(0)_UIButton(1)
UIView(0)_UILabel(2)  

混合

可以窥见,唯一层级路径已经为转,但是总体页面也无发生变化,不仅会发出新的风波(比如UIButton(0),UILabel(2)),连UIButton(1)事件的征集也会错,即使是差的事件,却抱了不同之eventID,所以要增强组织的层级结构路径的稳健型。

正好像刚刚提到的,不同品种的UIView不待举行index的别,那么当取得这index的时节,不是简约的从subviews这个数组中收获其对应之索引值,而是进行一个简练的同类归并再次取索引值,一个坏简单的拍卖。

for (UIView *view in subviews) {
    if ([NSStringFromClass([subview class]) isEqualToString:NSStringFromClass(class)]) { //class为待筛选的类
        [array addObject:view];
    }
}

这样就是得得array中的index作为那个确实的索引值,得到的层级结构路径也:

UIView(0)_UILabel(0)
UIView(0)_UIButton(0)
UIView(0)_UIButton(1) 

这时无论UIlabel的职务在哪儿,都未会见改变这路子的结构样式,大大加了稳健型。其实呢能发现,这只是只能加强稳健型,并无可知从根本上解决者题材,比如要自己把少只UIButton的次第调换了,或者去除了第一独,此时还是会博得部分未确切的层级路径。此题材会见连续解决,会日趋引入误差容量和相似度这个定义,即要以误差范围外,则会进行更进一步的相当,具体的解决方案本篇不以介绍。

哼吧,其实这复制粘贴代码这么难用自要坚持在手动一行一行复制粘贴下来空格敲的这样完美,都是为自己的好对象于拉扯我选我无限欣赏吃的樱桃准备被我寄过来!我忍不住哼起歌来~一想到马上呀~就受自身快乐~贴一张图馋馋你们哈哈哈!

3.3.1 UITextField

UITextField是UIControl的一个子类,由于UITextField涉及到用户之苦比较多,比如用户称、密码、聊天文本等,所以HubbleData不会见指向该类的UITextField进行埋点的采。

HubbleData主要收集的是UISearchBar中之UITextField,即UISearchBarTextField,并得到搜索的文本内容,这对部分电商类的App来说,能够比好的分析用户感兴趣之货等,这是作HubbleData
SDK无埋点的一个要求。

hook住sendAction:to:forEvent:后,如果对UISearchBarTextField的所有actions都进行hook的话,那么_searchFieldBeginEditing、_searchFieldEndEditing等具备的action发生的时节都见面进展数据的搜集,会采集到无数失效的音讯,导致采集的数额错乱。HubbleData
SDK只有当_searchFieldEndEditing
action发生常才见面开展埋点,收集之properties为:

(1) type 为UIControl采集的事件类型,这里设置为searchBarEvent;
(2) page 为当前页面的名称,用于前端显示用;
(3) searchText 为_searchFieldEndEditing发生时采集到搜索框的搜索文字(此字段不为空);

诸如此类即使能够针对搜索框进行无埋点采集,并能搜集搜索的文本内容。此方就是以_searchFieldEndEditing发生常采访数据,有或该action执行时无尽兴真正的搜操作,可能会见跟工作数据库的数量发生出入,但是也能较规范之剖析用户感兴趣的探寻内容。

通过 initWithFrame 初始化控件的时候传上的 frame
计算饼图的半径,根据计算出来的半径确定各个 view 的布阵位置:

0 引言

近年于背店之HubbleData的埋点SDK的付出任务,产品之雏形其实当几年前纵曾出了,公司里的如考拉、易信、LOFTER、美学、漫画等大多款产品还曾接入使用。

下图为出HubbleData SDK某个应用之一对分析的显得页面:

(1)概览示意图

事件

(2)事件分析示意图

事件

(3)实时分析示意图

事件

此外HubbleData平台还拥有在分析、漏斗分析、粘性分析、数据看板等多职能,方便有关负责人员对成品用户作为开展更的追分析。

镇版本的SDK的筹划是代码埋点实现之,虽然对于片较为成熟的制品,代码埋点完全会达标产品在的需要,但是对于部分新启动要用数转移的要求的新产品相当,考虑到其保护的资金非常,代价高等缺点,HubbleData无埋点SDK的计划虽显示更加重大了。

自根本承担iOS端无埋点以及可视化圈选的行事,文章主要系讲授一下HubbleData无埋点SDK在iOS端的筹划以及落实和片系题材的解决,后续将对准任何埋点的兑现流程及可视化圈选等内容还发作分享。

WS(weakSelf);

_pie = [[PieChart alloc] initWithFrame:CGRectZero];

_pie.dataSource = self;

[self.view addSubview:_pie];

[_pie mas_makeConstraints:^(MASConstraintMaker *make) {

    make.edges.equalTo(weakSelf.view);

}];

3.3 UIControl的无埋点采集

针对UIControl,HubbleData采用的凡hook
UIControl的sendAction:to:forEvent:方法。由法定文档可知,在UIControl执行相应之action时还见面率先调用sendAction:to:forEvent:方法,实现如下:

control

考虑到UIControl的子类较多,所以HubbleData选取了内部以于多之几乎种进行了独特的辨析:主要是UITextField、UIButton和UISwitch,其余的暂行不举行特别分析。具体的埋点的采访计划吧:

无哪种UIControl,EventID均以的凡第三部分介绍的绝无仅有标识字符串的SHA256编码值,但是相关采访properties有所差异。

本人曾经休思再说什么啊,这个代码为毛只能一行一行的复制粘贴进去什么!并且自己的空格怎么还丢掉了啊!我是手动的讹的这些空格啊!!手都争先抽筋了呀什么什么!!!

(2) 各个子页面的controller相同?

其实开了此类页面的核心应该还如数家珍,很多气象下子页面都是集体的,只不过是填的model不同而已,那么遇到这种场面,如果是准问题1底缓解思路,即使如约2.2将到了眼前页面的controller,那么还是无法区分出这些页面,所以还是用安装新的有辨识度的index。

实质上通过pageViewController可以发现,用户可经左右滑或者前后滑动来切换子页面,说明有的子页面都是坐在一个scrollView之中,那么就算可以由这个scrollView入手,重新规定index。下面给出HubbleData解决是题材之方。

同等开始想使用时scrollView的contentOffset整除此pageViewController的页面宽度和可观所获得的值当区分子页面的index,但是考虑到或contentOffset的连日变与子页面横跨pageViewController整数加倍宽度的边际时,可能会见招获取之index不唯一的气象,所以后来动该子页面的起首位置整除pageViewController的附和地宽度与惊人得到相应地index。具体的贯彻如下,其中controller为当前之页面:

 if (view == controller.view || view == controller.view.superview) {
      NSInteger index_x = view.center.x / [view superview].frame.size.width;
      NSInteger index_y = view.center.y / [view superview].frame.size.height;
      NSString *path = [NSString stringWithFormat:@"%@(indexx:%ld indexy:%ld)",  
                        NSStringFromClass([view class]), index_x, index_y];
  } 

用一律对上述(1)所被起之季独ViewController1,优化后底交之绝无仅有的标识为:

ViewController1对应路径为:superview(0)_subControllerView(indexx:0 indexy:0)
ViewController2对应路径为:superview(0)_subControllerView(indexx:1 indexy:0)
ViewController3对应路径为:superview(0)_subControllerView(indexx:2 indexy:0)
ViewController4对应路径为:superview(0)_subControllerView(indexx:3 indexy:0)

如此这般就是各个子页面的controller相同,也能够通过优化后的index来区别各个不同之子页面。当然这种唯有是针对嵌套scrollView的子页面的场面,不过会缓解大部分之此类问题,对于部分其他的与众不同情况等,需详细分析页面布局进行剖析。

一旦你的种类受到尚从未 Masonry,你得通过第三在管理平台 Cocoapods
来下载,如果发趣味的话,可以拘留一下自我事先整理的有关 Cocoapods
下载和安装之章: IOS依赖管理 –
CocoaPods.

2.2 当前页面controller的获

看起来,大多数景象下2.1底view的层级结构path已经主导确定view的绝无仅有标识字符串,但是普遍存在这么一栽状态,当同一个页面跳反两个不同的页面时,假如这片只例外的页面上都赢得第一单按钮的层级路径,得到的简化后底结果都如下所示:

.../UINavigationTransitionView(0)/UIViewControllerWrapperView(0)/UIView(0)/UIButton(0)

凡力不从心进行即时有限只页面及之按钮区分的,其实页面的类名是别的一个最好直白的不二法门。HubbleData是依下面的方法取得有view所在的controller的类名的。

赢得当前controller示例

拿view的层级路径结合当前页面的名称,已经能化解掉大部分底绝无仅有标识字符串的题材了。

这里需要小心的某些凡,当页面类型一样,只是填充的model不又,比如浏览商品详情时,所进的页面还是一个,只是model不同,目前HubbleData对这种景象小未举行拍卖。后续可参看文章3.2节UIViewController的无埋点采集,对一部分页面,用户可以由定义诸如screenTitle的字段,定义该页面的名称,比如screenTitle包含产品唯一ID时,此时以该字段加入唯一标识字符串中即可区分。目前这块还不举行相关处理,这里只是供一个大概的化解思路。

即使如您看来底,我以初始化的时光并没有吃闹一个原则性好之管事之
frame,而是加了同样词约束规范,这同一词话的意就是是 pie 的分寸如和当前之
view 一样特别,是的,这一切都是自动的。

3.5 UIGestureRecognizer的无埋点采集

每当iOS开发中,经常会以部分手势来拍卖部分点击的操作,所以也发出必不可少对UIGestureRecognizer进行hook。HubbleData
并无是直接针对UIGestureRecognizer这个仿佛进行hook,而是hook
UIView类的addGestureRecognizer:方法,实现如下:

gesture

透过hook
addGestureRecognizer:方法,可以获取该UIView所添加的UIGestureRecognizer,这里只针对UITapGestureRecognizer和UILongPressGestureRecognizer进行处理,其他的手势暂勿做拍卖。得到相应的UIGestureRecognizer,添加一个action,当该手势执行的下,同样会履该action,在action中实施埋点的操作。

这边获得之是UIGestureRecognizer所在的UIView的唯一标识标识字符串编码作为EventID,采集的属性也:

(1) type 为UIGestureRecognizer采集的事件类型,这里设置为gestureTapEvent;
(2) page 为当前页面的名称,用于前端显示用;

UIAlertController的奇特处理

这里需要针对UIAlertController做一个详实的说明,因为UIAlertController在点击诸如取消、确定的精选项按钮时,也会进展手势的埋点采集,但是当iOS9和iOS10达成稍稍有若干区别。

这里先为iOS9啊例,其target是作用在_UIAlertControllerView这个体系的私有类上之,如果直接对这_UIAlertControllerView进行唯一标识字符串的布局,则取消同确定选项得到的EventID是一样的,这样以无法精确之辨析产生用户的选取,所以必须坐每个选项view作为单身的绝无仅有标识字符串进行分析才能够准确区分。通过获得_UIAlertControllerView的_actionViews变量,就可知取各个选项之view,这里要举行一个简便的点击坐标获取,判断所点击的区域位于的actionView,具体实现如下:

此地以标准化判断时设定gesture.state ==
UIGestureRecognizerStateBegan,是由于UILongPressGestureRecognizer会连续两不良调用action,因此这里用参加事件之状态进行区分,防止进行有限糟糕同的数目收集。

iOS10下之UIAlertController的里贯彻做了一部分变动,其target变更换成于_UIAlertControllerInterfaceActionGroupView这个体系的私有类上之,然后用开展定的拍卖,获取UIInterfaceActionSelectionTrackingController的_representationViews变量,遍历得到各个选项的view,具体贯彻如下:

经上述的辨析好发现,这样虽然能分和一个UIAlertController的差的操作选项,但是也许无法区分出不同UIAlertController的处同一职务的选项项,所以这里还要参加UIAlertController额外之习性信息来区别。

面前吧发出提了,可以死易之想到UIAlertController的message和title能够比好的进展分,所以于原来的层级路径和当下页面的功底及,还要加上message和title以整合唯一标识字符串。给起一个样例:

path(UIWindow(0)__UIAlertControllerView(0)_UIView(0)_UIView(0)_UIView(0)_UICollectionView(0)__UIAlertControllerCollectionViewCell(section:0 item:0)_UIView(0)__UIAlertControllerActionView(0))&controller(UIAlertController)&message(确认退出群聊吗?)&title(退群)

– (id)initWithFrame:(CGRect)frame{

    self = [super initWithFrame:frame];

    if(self){

         self.backgroundColor = [UIColor clearColor];

         //UIScollView 容器

         _scrollContentView = [[UIScrollView alloc]
initWithFrame:self.bounds];

         _scrollContentView.backgroundColor = [UIColor clearColor];

         [self addSubview:_scrollContentView];

         //饼图

         _pieView = [[UIView alloc] initWithFrame:self.bounds];

         _pieView.backgroundColor = [UIColor clearColor];

         [_scrollContentView addSubview:_pieView];

         //饼图中心白色信息区域

         _infoView = [[UIView alloc] initWithFrame:self.bounds];

         _infoView.backgroundColor = [UIColor whiteColor];

         [_scrollContentView addSubview:_infoView];

         //颜色说明

         _descriptionView = [[UIView alloc]
initWithFrame:self.bounds];

         _descriptionView.backgroundColor = [UIColor clearColor];

         [_scrollContentView addSubview:_descriptionView];

         //计算饼图半径

         self.pieRadius = MIN(self.bounds.size.width/2,
self.bounds.size.height/2) – kMarginX*2;

         self.pieCenter = CGPointMake(self.bounds.size.width/2,
_pieRadius + kMarginY);

         _animationArr = [NSMutableArray array];

         self.textRadius = _pieRadius –
(_pieRadius-kInfoRadius)/2;

         }

    return self;

}

– (void)setPieCenter:(CGPoint)pieCenter{

    [_pieView setCenter:pieCenter];

    [_infoView setCenter:pieCenter];

    //这个点是惠及用来当饼图上面画扇叶用的,是以饼图也原则的中心值

    _pieCenter = CGPointMake(_pieView.frame.size.width/2,
_pieView.frame.size.height/2);

}

– (void)setPieRadius:(CGFloat)pieRadius{

    _pieRadius = pieRadius;

    CGRect frame = CGRectMake(_pieCenter.x – pieRadius, _pieCenter.y

  • pieRadius, pieRadius*2, pieRadius*2);

    _pieCenter = CGPointMake(frame.size.width/2,frame.size.height/2);

    //设置饼图 frame

    [_pieView setFrame:frame];

    [_pieView.layer setCornerRadius:_pieRadius];

    CGFloat infoRadius = kInfoRadius;

    frame = CGRectMake(_pieCenter.x – infoRadius, _pieCenter.y –
infoRadius, infoRadius*2, infoRadius*2);

    //设置白色信息图 frame

    [_infoView setFrame:frame];

    [_infoView.layer setCornerRadius:infoRadius];

}

早期都铺垫了了,接下去我们就是省自家是什么样将本可以的代码改成为了
Autolayout 的不错的代码的了