前言

由于UIGestureRecognizer是一个抽象基类,所以它并不会处理具体的手势。因此,对于具体的手势触摸事件,需要使用相应的子类进行处理。
常见的具体手势识别器
UITapGestureRecognizer:轻拍手势
UILongPressGestureRecognizer:长按手势
UIPinchGestureRecognizer:捏合(缩放)手势
UIRotationGestureRecognizer:旋转手势
UISwipeGestureRecognizer:轻扫手势
UIPanGestureRecognizer:平移手势
UIScreenEdgePanGestureRecognizer:屏幕边缘平移手势
I、手势全埋点方案
接下来介绍如何实现手势识别的全埋点,来采集常见控件(UILabel、UIImageView)的轻拍和长按手势。
手势识别器使用了Target-Action设计模式,可调用UIView类中的-addGestureRecognizer:
方法进行添加手势识别器
当我们为一个手势识别器添加一个或多个Target-Action后,在视图上进行触摸操作时,一旦系统识别了该手势,就会向所有的Target(对象)发送消息,并执行Action(方法)
主要是通过如下两个方法进行添加手势识别器
- initWithTarget:target action:
- addTarget:action:
addGestureRecognizer的使用例子
_tappedLabel.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[tap addTarget:self action:@selector(tapAction:)];
[_tappedLabel addGestureRecognizer:tap];
虽然手势操作和UIControl类一样,均使用了Target-Action设计模式,但手势识别器并不会将消息交由UIApplication对象来发送。因此无法使用与UIControl控件相同的处理方式(通过响应者链的方式来实现全埋点)
为了采集控件的轻拍手势,我们可以通过Method Swizzling在UITapGestureRecognizer类中添加Target-Action的方法,添加一个新的Target-Action,并在新添加的Action中触发Click事件,进而实现控件轻拍手势全埋点。
1.1 轻拍手势全埋点
#import "UIGestureRecognizer+SensorsData.h"
#import "SensorsAnalyticsSDK.h"
#import "NSObject+SASwizzler.h"
@implementation UITapGestureRecognizer (SensorsData)
+ (void)load {
// Swizzle initWithTarget:action: 方法
[UITapGestureRecognizer sensorsdata_swizzleMethod:@selector(initWithTarget:action:) withMethod:@selector(sensorsdata_initWithTarget:action:)];
// Swizzle addTarget:action: 方法
[UITapGestureRecognizer sensorsdata_swizzleMethod:@selector(addTarget:action:) withMethod:@selector(sensorsdata_addTarget:action:)];
}
- (instancetype)sensorsdata_initWithTarget:(id)target action:(SEL)action {
// 调用原始的初始化方法进行对象初始化
[self sensorsdata_initWithTarget:target action:action];
// 调用添加 Target-Action 方法,添加埋点的 Target-Action
// 这里其实调用的是 sensorsdata_addTarget:action: 里的实现方法,因为已经进行了 swizzle
[self addTarget:target action:action];
return self;
}
- (void)sensorsdata_addTarget:(id)target action:(SEL)action {
// 调用原始的方法,添加 Target-Action
[self sensorsdata_addTarget:target action:action];
// 新增 Target-Action,用于埋点
[self sensorsdata_addTarget:self action:@selector(sensorsdata_trackTapGestureAction:)];
}
- (void)sensorsdata_trackTapGestureAction:(UITapGestureRecognizer *)sender {
// 获取手势识别器的控件
UIView *view = sender.view;
// 暂只采集 UILabel 和 UIImageView
BOOL isTrackClass = [view isKindOfClass:UILabel.class] || [view isKindOfClass:UIImageView.class];
if (!isTrackClass) {
return;
}
// Click事件的属性
NSDictionary *properties = @{@"$element_type": NSStringFromClass(self.class)};
// 触发Click 事件
[[SensorsAnalyticsSDK sharedInstance] trackAppClickWithView:view properties:properties];
}
@end
1.2 长按手势全埋点
手势处于UIGestureRecognizerStateEnded状态时,才触发Click事件
@implementation UILongPressGestureRecognizer (SensorsData)
+ (void)load {
// Swizzle initWithTarget:action: 方法
[UILongPressGestureRecognizer sensorsdata_swizzleMethod:@selector(initWithTarget:action:) withMethod:@selector(sensorsdata_initWithTarget:action:)];
// Swizzle addTarget:action: 方法
[UILongPressGestureRecognizer sensorsdata_swizzleMethod:@selector(addTarget:action:) withMethod:@selector(sensorsdata_addTarget:action:)];
}
- (instancetype)sensorsdata_initWithTarget:(id)target action:(SEL)action {
// 调用原始的初始化方法进行对象初始化
[self sensorsdata_initWithTarget:target action:action];
// 调用添加 Target-Action 方法,添加埋点的 Target-Action
// 这里其实调用的是 sensorsdata_addTarget:action: 里的实现方法,因为已经进行了 swizzle
[self addTarget:target action:action];
return self;
}
- (void)sensorsdata_addTarget:(id)target action:(SEL)action {
// 调用原始的方法,添加 Target-Action
[self sensorsdata_addTarget:target action:action];
// 新增 Target-Action,用于埋点
[self sensorsdata_addTarget:self action:@selector(sensorsdata_trackLongPressGestureAction:)];
}
- (void)sensorsdata_trackLongPressGestureAction:(UILongPressGestureRecognizer *)sender {
if (sender.state != UIGestureRecognizerStateEnded) {
return;
}
// 获取手势识别器的控件
UIView *view = sender.view;
// 暂定只采集 UILabel 和 UIImageView
BOOL isTrackClass = [view isKindOfClass:UILabel.class] || [view isKindOfClass:UIImageView.class];
if (!isTrackClass) {
return;
}
NSDictionary *properties = @{@"$element_type": NSStringFromClass(self.class)};
// 触发Click 事件
[[SensorsAnalyticsSDK sharedInstance] trackAppClickWithView:view properties:properties];
}
@end
II 、右划返回的事件与scrollView滚动事件冲突的解决方案
-
通过 requireGestureRecognizerToFail方法来处理。
将两个手势依次处理, 一个校验失败,再执行另外一个手势的校验,以解决手势冲突.
[_bigScrollView.panGestureRecognizer requireGestureRecognizerToFail:self.navigationController.interactivePopGestureRecognizer];
see also
iOS为UITableView添加UISwipeGestureRecognizer手势(尤其适用于多个cell 之间的切换)
https://kunnan.blog.csdn.net/article/details/77652738
本文分享自微信公众号 - iOS逆向(code4ios)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。