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
相关推荐
每天被AI救一次1 小时前
WorkBuddy 会议自动化实战:从腾讯会议转写到待办系统的完整链路(附全套提示词)
运维·自动化·腾讯会议
林墨聊AIGC1 小时前
AI视频怎么做跳舞的动作效果:从入门到精通的舞蹈动画制作指南
大数据·人工智能·自动化·aigc·音视频
chuntian_tester2 小时前
AI自动化第3步【用例设计】
人工智能·测试工具·ai·自动化
麒麟马3 小时前
Swift 扩展归纳---Array (一)
ios·swift
此冬歌咏4 小时前
自动化服务器运维监控系统(python+shell)
linux·运维·服务器·开发语言·python·自动化
自动化监测Learner4 小时前
自动化监测数据传不出廊道?大坝通信组网选型实战:RS485、光纤、4G、LoRa、北斗短报文一篇讲透
运维·网络·自动化
HAHAXX84 小时前
2026智能自动化落地:通义灵码与Cursor加持,RPA融合生成式AI的工程化实践
人工智能·自动化·rpa
chuntian_tester4 小时前
AI自动化第1步【系统探索】
人工智能·测试工具·ai·自动化
可乐鸡翅yeah_5 小时前
HLS 自动化拨测与手动调试分工,流媒体线上监控体系建设实践
运维·自动化·测试用例·音视频·媒体·m3u8·音视频在线播放
PFFstronger5 小时前
从 0 到 1 搭建接口自动化测试框架:分层架构 + 数据驱动 + 接口依赖编排
python·架构·自动化