UIButton防止按钮和手势的暴力点击

Posted walkerwqp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UIButton防止按钮和手势的暴力点击相关的知识,希望对你有一定的参考价值。

首先理解下几个概念
1、IMP:它是指向一个方法具体实现的指针,每一个方法都有一个对应的IMP,当你发起一个消息之后,最终它会执行的那段代码,就是由IMP这个函数指针指向了这个方法实现的
2、SEL:方法名称的描述,只记录方法的编号不记录具体的方法,具体的方法是 IMP
3、Method:是一个类实例,里面的结构体有一个方法选标 SEL – 表示该方法的名称,一个types – 表示该方法参数的类型,一个 IMP - 指向该方法的具体实现的函数指针。

针对UIButton、UISegmentedControl、UISwitch这些继承自UIControl的控件可通过hook sendAction:to:forEvent:这个方法实现控件的重复点击
具体思路:
1、创建UIButton的类别,使用runtime添加public属性eventInterval作为计时因子
2、添加private属性eventUnavailable来控制button的点击事件是否有效
3、在+load方法中实现系统sendAction:to:forEvent:方法与自定义方法进行交换
代码实现:

+ (void)load 
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
        Method origMethod = class_getInstanceMethod([self class], @selector(sendAction:to:forEvent:));
        SEL origsel = @selector(sendAction:to:forEvent:);
        Method swizMethod = class_getInstanceMethod([self class], @selector(tk_sendAction:to:forEvent:));
        SEL swizsel = @selector(tk_sendAction:to:forEvent:);
        BOOL addMehtod = class_addMethod([self class], origsel, method_getImplementation(swizMethod), method_getTypeEncoding(swizMethod));
        
        if (addMehtod) 
            class_replaceMethod([self class], swizsel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
         else 
            method_exchangeImplementations(origMethod, swizMethod);
        
    );


- (void)tk_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event 
    if (self.eventUnavailable == NO) 
        self.eventUnavailable = YES;
        [self tk_sendAction:action to:target forEvent:event];
        [self performSelector:@selector(setEventUnavailable:) withObject:@(NO) afterDelay:self.eventInterval];
    

针对单击事件可对UITapGestureRecognizer的initWithTarget:action:或addTarget:action:进行hook
具体思路:
1、创建UITapGestureRecognizer的类别,使用runtime添加public属性eventInterval作为计时因子
2、添加private属性eventUnavailable来控制button的点击事件是否有效
3、在+load方法中实现系统initWithTarget:action:方法与自定义方法进行交换
4、将target和selector关联到创建的类别中并且将selector替换成类别中自定义的响应方法
代码实现:

- (instancetype)initTKWithTarget:(id)target action:(SEL)action 
    self = [self initTKWithTarget:self action:@selector(tap:)];
    objc_setAssociatedObject(self, gestureTargetKey, target, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    objc_setAssociatedObject(self, gestureSelKey, NSStringFromSelector(action), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    return self;


- (void)tap:(UIGestureRecognizer *)tapGesture 
    id target = objc_getAssociatedObject(self, gestureTargetKey);
    SEL action = NSSelectorFromString(objc_getAssociatedObject(self, gestureSelKey));
    
    if (self.eventUnavailable == NO) 
        self.eventUnavailable = YES;
        [target performSelector:action];
        [self performSelector:@selector(setEventUnavailable:) withObject:@(NO) afterDelay:self.eventInterval];
    

针对单击事件的第二种实现方式实现带参数的init方法
具体思路:
1、在分类中实现initWithTarget:action:eventIntervl:的方法通过传进去计时因子控制点击事件是否可以响应,将手势的代理设置成新建的分类
2、添加属性eventUnavailable来控制点击事件是否有效
3、通过重写gestureRecognizer:shouldReceiveTouch:来控制点击事件是否响应
代码实现:

-(instancetype)initWithTarget:(id)target action:(SEL)action eventIntervl:(NSTimeInterval)eventIntervl 
    self = [super init];
    if (self) 
        self.eventInterval = eventIntervl;
        self.delegate = self;
        [self addTarget:target action:action];
    
    return self;


- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
    if (self.eventUnavailable == NO) 
        self.eventUnavailable = YES;
        [self performSelector:@selector(setEventUnavailable:) withObject:@(NO) afterDelay:self.eventInterval];
        return YES;
     else 
        return NO;
    

 



 

以上是关于UIButton防止按钮和手势的暴力点击的主要内容,如果未能解决你的问题,请参考以下文章

UIButton 和滑动手势

如何通过让子视图 UIButton 获取新闻事件?

防止多次点击同一个 UIButton

识别禁用的 UIButton 上的单击或手势事件

Closed:: 创建一个带有滑动和点击手势的 UIButton 水平轮播

捕获组合的按钮按下和平移手势