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
相关推荐
QuantumLeap丶2 小时前
《Flutter全栈开发实战指南:从零到高级》- 26 -持续集成与部署
android·flutter·ios
星星泡饭2924 小时前
极端环境生存指南——针对极寒、高海拔及强震动环境的连接件选型与合规评估
自动化·硬件工程·制造
GAOJ_K4 小时前
滚柱导轨中的密封件如何判断是否需更换?
运维·人工智能·科技·自动化·制造
2501_915918415 小时前
TCP 抓包分析在复杂网络问题中的作用,从连接和数据流层面理解系统异常行为
网络·网络协议·tcp/ip·ios·小程序·uni-app·iphone
天空属于哈夫克35 小时前
从自动化到智能化:企业微信 RPA 的未来演进与行业思考
自动化·企业微信·rpa
2501_924064115 小时前
2025年一站式测试平台对比:可视化报告与自动化监控最佳实践
运维·自动化
二流小码农6 小时前
鸿蒙开发:个人开发者如何使用华为账号登录
android·ios·harmonyos
乾元7 小时前
基于时序数据的异常预测——短期容量与拥塞的提前感知
运维·开发语言·网络·人工智能·python·自动化·运维开发
企微自动化7 小时前
企业微信自动化系统稳定性优化实战
运维·自动化·企业微信
认真的小羽❅8 小时前
Python Selenium 超详细新手教程:从零开始掌握浏览器自动化
selenium·测试工具·自动化