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
相关推荐
BullSmall1 小时前
规则+大模型混合评测:高效精准双保障
运维·自动化
楚识科技7 小时前
合同比对全流程自动化:OCR识别+差异比对+法律风险字段抽取实战指南
运维·自动化·ocr
代码方舟7 小时前
零信任架构实战:基于天远人企关联构建自动化供应链金融网关
运维·人工智能·架构·自动化
天远API8 小时前
零信任架构实战:基于天远人企关联构建自动化图谱网关
网络·人工智能·架构·自动化
传奇开心果编程8 小时前
【SwiftUI娓娓道来】第3课:让界面活起来——交互与动画指南
学习·macos·ui·ios·swiftui·swift
可乐鸡翅yeah_9 小时前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放
朋友圈自动点赞工具14 小时前
Gmail 委托邮箱在手机上关不掉
macos·智能手机·cocoa·硬件评测
海盗123414 小时前
AI 新闻日报 2026-09-28:正确性审查自动化、7×24 个人智能体、具身部署态
运维·人工智能·机器人·自动化·人工智能aigc
00后程序员张15 小时前
苹果App Store上架指南:费用、原理与步骤详解
android·ios·小程序·https·uni-app·iphone·webview
dghongcheng15 小时前
UV固化设备怎么选?UV固化炉选购指南
自动化·喷涂设备·喷涂·喷漆设备·自动喷涂线