iOS UIGestureRecgonizer自动化点击埋点

项目中很多情况下视图的点击点是通过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
相关推荐
云手机管家2 小时前
CDN加速对云手机延迟的影响
运维·服务器·网络·容器·智能手机·矩阵·自动化
云手机管家2 小时前
账号风控突破:云手机设备指纹篡改检测与反制技术解析
android·运维·网络协议·网络安全·智能手机·矩阵·自动化
若水无华11 小时前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
Aress"12 小时前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
struggle202512 小时前
AgenticSeek开源的完全本地的 Manus AI。无需 API,享受一个自主代理,它可以思考、浏览 Web 和编码,只需支付电费。
人工智能·开源·自动化
北漂老男孩13 小时前
ChromeDriver 技术生态与应用场景深度解析
java·爬虫·python·自动化
逸雨清风20 小时前
Chrome更新到136以后selenium等自动化浏览器失效
selenium·自动化
Jouzzy1 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克1 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨1 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3