iOS 通过runtime swizzling实现button重

作者: 智人一千 | 来源:发表于2019-09-26 12:28 被阅读0次

    问题

    通过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重

        本文链接:https://www.haomeiwen.com/subject/xtbguctx.html