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
相关推荐
Jinkxs7 小时前
自动化测试的下一站:AI缺陷检测工具如何实现“bug提前预警”?
人工智能·自动化
YungFan8 小时前
iOS26适配指南之UIButton
ios·swift
myzzb8 小时前
基于uiautomation的自动化流程RPA开源开发演示
运维·python·学习·算法·自动化·rpa
许泽宇的技术分享10 小时前
Windows MCP.Net:基于.NET的Windows桌面自动化MCP服务器深度解析
windows·自动化·.net
红橙Darren12 小时前
手写操作系统 - 编译链接与运行
android·ios·客户端
鹏多多.16 小时前
flutter-使用device_info_plus获取手机设备信息完整指南
android·前端·flutter·ios·数据分析·前端框架
张3蜂16 小时前
深度解读 Browser-Use:让 AI 驱动浏览器自动化成为可能
运维·人工智能·自动化
张3蜂1 天前
Stagehand深度解析:从开源自动化工具到企业级RPA平台的演进之路
开源·自动化·rpa
麦兜*1 天前
【swift】SwiftUI动画卡顿全解:GeometryReader滥用检测与Canvas绘制替代方案
服务器·ios·swiftui·android studio·objective-c·ai编程·swift
qq_526099131 天前
工控机的用途与介绍:工业自动化的重要引擎
嵌入式硬件·自动化·电脑