一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情。
Target-Action设计模式
在具体介绍如何实现之前,我们需要先了解在UIKit框架下点击或拖动 事件的Target-Action设计模式。
Target-Action模式主要包含两个部分。
Target(对象):接收消息的对象。Action(方法):用于表示需要调用的方法
Target可以是任意类型的对象。但是在iOS应用程序中,通常情况下会 是一个控制器,而触发事件的对象和接收消息的对象(Target)一样,也可 以是任意类型的对象。例如,手势识别器UIGestureRecognizer就可以在识 别到手势后,将消息发送给另一个对象。
当我们为一个控件添加Target-Action后,控件又是如何找到Target并执 行对应的Action的呢?
UIControl类中有一个方法:
- (void)sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event;
用户操作控件(比如点击)时,首先会调用这个方法,并将事件转发 给应用程序的UIApplication对象。
同时,在UIApplication类中也有一个类似的实例方法:
- (BOOL)sendAction:(SEL)action to:(nullable id)target from:(nullable id)sender forEvent:(nullable UIEvent *)event;
如果Target不为nil,应用程序会让该对象调用对应的方法响应事件;如果Target为nil,应用程序会在响应链中搜索定义了该方法的对象,然后 执行该方法。
基于Target-Action设计模式,有两种方案可以实现$AppClick事件的全埋点。下面我们将逐一进行介绍。
方案一
描述
通过Target-Action设计模式可知,在执行Action之前,会先后通过控件 和UIApplication对象发送事件相关的信息。因此,我们可以通过Method Swizzling交换UIApplication类中的-sendAction:to:from:forEvent:方法,然后 在交换后的方法中触发$AppClick事件,并根据target和sender采集相关属性,实现$AppClick事件的全埋点。
代码实现
新建一个UIApplication的分类
+ (void)load {
[UIApplication sensorsdata_swizzleMethod:@selector(sendAction:to:from:forEvent:) withMethod:@selector(CountData_sendAction:to:from:forEvent:)];
}
- (BOOL)CountData_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event {
[[SensorsAnalyticsSDK sharedInstance] track:@"$appclick" properties:nil];
return [self CountData_sendAction:action to:target from:sender forEvent:event];
}
一般情况下,对于一个控件的点击事件,我们至少还需要采集如下信息(属性):
- 控件类型(
$element_type) - 控件上显示的文本(
$element_content) - 控件所属页面(
$screen_name)
获取控件类型
先为你介绍一下NSObject对象的继承关系图
从上图可以看出,控件都是继承于UIView,所以获取要想获取控件类型,可以声明UIView的分类
新建UIView的分类(UIView+TypeData)
UIView+TypeData.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (TypeData)
@property (nonatomic,copy,readonly) NSString *elementType;
@end
NS_ASSUME_NONNULL_END
UIView+TypeData.m
#import "UIView+TypeData.h"
@implementation UIView (TypeData)
- (NSString *)elementType {
return NSStringFromClass([self class]);
}
@end
获取控件类型的埋点实现
+ (void)load {
[UIApplication sensorsdata_swizzleMethod:@selector(sendAction:to:from:forEvent:) withMethod:@selector(CountData_sendAction:to:from:forEvent:)];
}
- (BOOL)CountData_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event {
UIView *view = (UIView *)sender;
NSMutableDictionary *prams = [[NSMutableDictionary alloc]init];
//获取控件类型
prams[@"$elementtype"] = view.elementType;
[[SensorsAnalyticsSDK sharedInstance] track:@"$appclick" properties:prams];
return [self CountData_sendAction:action to:target from:sender forEvent:event];
}