iOS 通过runtime swizzling实现button重复点击限制(附demo)

问题

通过runtime的swizzling特性实现button重复点击限制,网上相关内容比较多。
但是都是千篇一律的代码,在UIButton的分类category,交换sendAction:to:forEvent:方法,
如:


网络上通常实现方式

这种实现有什么问题呢?

如下图:
当代码内有textfield且实现了输入监听,在输入文本过程中会因为找不到方法而崩溃


textfield输入时候
崩溃

原因及解决方案

原因

如下图,sendAction:to:forEvent:是UIControl的方法,我们替换了该方法实现,其他使用到该方法的类也会走到被替换的方法内,但是此时我们的替换方法是在UIButton的category内定义的,其他类就找不到这个替换方法,所以发生崩溃!


UIControl类.png

解决方案

我们知道sendAction:to:forEvent:是UIControl的方法,且UIButton和UITextfield等使用到sendAction:to:forEvent:方法的类都是继承自UIControl,那可不可以在UIControl的分类去交换sendAction:to:forEvent:实现呢?内部通过类型判断[self isKindOfClass:[UIButton class]]才处理,这样上面找不sendAction:to:forEvent:替换方法的问题就解决了!顺着这思路我们往下看代码实现:
在UIControl的category内:
swizzling核心代码

+(void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSel = @selector(sendAction:to:forEvent:);
        SEL swizzlingSel = @selector(em_sendAction:to:forEvent:);
        
        Method originalMethod = class_getInstanceMethod(class, originalSel);
        Method swizzlingMethod = class_getInstanceMethod(class, swizzlingSel);
        
        /*添加原有方法originalSel,如果添加成功则说明该类没有原有方法originalSel,是继承自己父类的
         *实现原有方法的imp,通过class_replaceMethod替换swizzlingSel实现为originalMethod,这时originalSel-->swizzlingMethod的IMP,swizzlingSel-->originalMethod的IMP
         */
        BOOL isAddMethod = class_addMethod(class, originalSel, method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod));
        
        if (isAddMethod) {
            class_replaceMethod(class, swizzlingSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }else{
            method_exchangeImplementations(originalMethod, swizzlingMethod);
        }
        
        
    });
}

点击限制,防止快速重复点击核心代码:

-(void)em_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    if ([self isKindOfClass:[UIButton class]]) {//wct20190925 textfield输入时候崩溃添加判断,只管理button的点击事件
        if (self.isNeedDelayClick) {//需要延迟点击
            
            if (self.timeInteralOfClickButton <= 0) {//没设置时间间隔,默认为0.4秒
                self.timeInteralOfClickButton = 0.4;
            }
            
            //当前时间减上次点击时间,间隔大于规定时间间隔,button可点击
            BOOL isCanAction = NSDate.date.timeIntervalSince1970 - self.timeInteralEventLastTime >= self.timeInteralOfClickButton;
            
            if (self.timeInteralOfClickButton > 0) {//更新当前点击时间
                self.timeInteralEventLastTime = NSDate.date.timeIntervalSince1970;
            }
            
            if (isCanAction) {
                [self em_sendAction:action to:target forEvent:event];
            }
        }else{
            [self em_sendAction:action to:target forEvent:event];
        }
    }else{
        [self em_sendAction:action to:target forEvent:event];
    }
    
}

通过这种方式实现,我们避免了其他涉及到sendAction:to:forEvent:引发的崩溃,代码更健壮,完美解决了button重复点击的问题。get到的朋友点个❤️

demo地址

你可能感兴趣的:(iOS 通过runtime swizzling实现button重复点击限制(附demo))