项目中很多情况下视图的点击点是通过UITapGestureRecognizer 来实现的,这个时候我们就也是可以支持自动化埋点的
思路如下:
通过运行时hook UITapgestureRecognizer的addTarget:action: 方法
和initWithTarget:action: 方法,在交换的方法中添加埋点的响应方法,
并保留原始的响应方法,在埋点响应方法中执行埋点方法,然后调用原来的响应
直接上代码,
@interface UIGestureRecognizer (Event)
@property (nonatomic, strong) NSString *actionName;
#pragma mark - Private
@property (nonatomic, strong) NSString *senderAction;
@end
//
// UIGestureRecognizer+Event.m
// TEXT
//
// Created by mac on 2025/3/2.
// Copyright © 2025 刘博. All rights reserved.
//
#import "UIGestureRecognizer+Event.h"
#import "NSObject+Swizzle.h"
static const void *monitorActionNameKey = "monitorActionNameKey";
static const void *KSenderAction = "KSenderAction";
static const char *KTargetActionPair = "KTargetActionPair";
@implementation UIGestureRecognizer (Event)
+ (void)load
{
[NSObject exchangeInstanceMethod:[self class] originalSel:@selector(addTarget:action:) swizzledSel:@selector(log_addTarget:action:)];
[NSObject exchangeInstanceMethod:[self class] originalSel:@selector(initWithTarget:action:) swizzledSel:@selector(initWithLogTarget:action:)];
}
- (void)log_addTarget:(id)target action:(SEL)action
{
//只记录点击事件
if ([self isKindOfClass:[UITapGestureRecognizer class]]) {
// 原因:UIWebView init时,系统API内部触发UIKit`-[UIWebBrowserView installGestureRecognizers]:
// 过滤部分系统私有手势
NSString *targetClsStr = NSStringFromClass([target class]);
NSString *gesClsStr = NSStringFromClass([self class]);
if(target && ( [targetClsStr hasPrefix:@"UIWeb"] || [gesClsStr hasPrefix:@"_UI"] || [gesClsStr hasPrefix:@"WKSynthetic"] || [gesClsStr hasPrefix:@"UIKB"]))
{
[self log_addTarget:target action:action];
return;
}
NSMapTable *targetActions = [self targetActionPair];
if (targetActions == nil ) {
targetActions = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory
valueOptions:NSPointerFunctionsWeakMemory];
[self setTargetActionPair:targetActions];
}
[targetActions setObject:target forKey:NSStringFromSelector(action)];
[self log_addTarget:self action:@selector(logAction:)];
//先记录action
[self setSenderAction:NSStringFromSelector(action)];
}else{
[self log_addTarget:target action:action];
}
}
- (void)logAction:(UIGestureRecognizer *) sender
{
UIView *view = self.view;
//执行埋点操作
NSLog(@"哈哈哈手势点击埋点了手势点击埋点了手势点击埋点了");
[self callOriginActions];
}
- (void)callOriginActions
{
NSMapTable *targetActions = [[self targetActionPair] copy];
NSEnumerator *enumerator = [targetActions keyEnumerator];
id selector;
while ((selector = [enumerator nextObject])) {
id target = [targetActions objectForKey:selector];
if (target != nil) {
SEL s = NSSelectorFromString(selector);
IMP imp = [target methodForSelector:s];
void (*func)(id, SEL,id) = (void *)imp;
func(target, s, self);
}
}
}
- (id)initWithLogTarget:(id)target action:(SEL)action
{
self = [self initWithLogTarget:nil action:NULL];
if (self) {
[self addTarget:target action:action];
}
return self;
}
- (NSString *)actionName {
return objc_getAssociatedObject(self, monitorActionNameKey);
}
- (void)setActionName:(NSString *)monitorActionName{
objc_setAssociatedObject(self, monitorActionNameKey,
monitorActionName,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)senderAction {
return objc_getAssociatedObject(self, KSenderAction);
}
- (void)setSenderAction:(NSString *)senderAction{
objc_setAssociatedObject(self, KSenderAction,
senderAction,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(void)setTargetActionPair:(NSMapTable *)targetActionPair
{
objc_setAssociatedObject(self, KTargetActionPair, targetActionPair, OBJC_ASSOCIATION_RETAIN);
}
-(NSMapTable*)targetActionPair
{
return objc_getAssociatedObject(self, KTargetActionPair);
}
@end