[iOS]利用runtime,解决多次点击相同button,导致

作者: ocarol | 来源:发表于2016-08-16 17:45 被阅读4529次

    场景

    当app有点卡的时候,多次点击相同的button,经常出现,跳转了N次相同的界面(比如闲鱼)

    解决办法

    用运行时和分类,替换UIControl响应事件,根据响应的间隔时间来判断是否执行事件。

    详细步骤

    1. 创建一个UIControl的分类
      Snip20160816_3.png
    Snip20160816_4.png

    为了方便他人调整不同的间隔时间需求,在UIControl+Custom.h文件中开放间隔时间属性,UIControl+Custom.h文件的代码为:

    //  UIControl+Custom.h
    //  Created by ocarol on 16/8/16.
    //  Copyright © 2016年 ocarol. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface UIControl (Custom)
    @property (nonatomic, assign) NSTimeInterval custom_acceptEventInterval;// 可以用这个给重复点击加间隔
    @end
    

    UIControl+Custom.m文件中实现方法交换(妥善的做法是:先添加方法,如果方法已经存在,就替换原方法),在UIControl+Custom.m文件的代码为:

    //  UIControl+Custom.m
    //  Created by ocarol on 16/8/16.
    //  Copyright © 2016年 ocarol. All rights reserved.
    //
    
    #import "UIControl+custom.h"
    #import <objc/runtime.h>
    
    @interface UIControl()
    @property (nonatomic, assign) NSTimeInterval custom_acceptEventTime;
    @end
    
    
    @implementation UIControl (Custom)
    
    + (void)load{
        Method systemMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
        SEL sysSEL = @selector(sendAction:to:forEvent:);
        
        Method customMethod = class_getInstanceMethod(self, @selector(custom_sendAction:to:forEvent:));
        SEL customSEL = @selector(custom_sendAction:to:forEvent:);
        
        //添加方法 语法:BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) 若添加成功则返回No
        // cls:被添加方法的类  name:被添加方法方法名  imp:被添加方法的实现函数  types:被添加方法的实现函数的返回值类型和参数类型的字符串
        BOOL didAddMethod = class_addMethod(self, sysSEL, method_getImplementation(customMethod), method_getTypeEncoding(customMethod));
        
        //如果系统中该方法已经存在了,则替换系统的方法  语法:IMP class_replaceMethod(Class cls, SEL name, IMP imp,const char *types)
        if (didAddMethod) {
            class_replaceMethod(self, customSEL, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
        }else{
            method_exchangeImplementations(systemMethod, customMethod);
            
        }
    }
    
    - (NSTimeInterval )custom_acceptEventInterval{
        return [objc_getAssociatedObject(self, "UIControl_acceptEventInterval") doubleValue];
    }
    
    - (void)setCustom_acceptEventInterval:(NSTimeInterval)custom_acceptEventInterval{
        objc_setAssociatedObject(self, "UIControl_acceptEventInterval", @(custom_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (NSTimeInterval )custom_acceptEventTime{
        return [objc_getAssociatedObject(self, "UIControl_acceptEventTime") doubleValue];
    }
    
    - (void)setCustom_acceptEventTime:(NSTimeInterval)custom_acceptEventTime{
        objc_setAssociatedObject(self, "UIControl_acceptEventTime", @(custom_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (void)custom_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
        
       // 如果想要设置统一的间隔时间,可以在此处加上以下几句
       // 值得提醒一下:如果这里设置了统一的时间间隔,会影响UISwitch,如果想统一设置,又不想影响UISwitch,建议将UIControl分类,改成UIButton分类,实现方法是一样的
       // if (self.custom_acceptEventInterval <= 0) {
       //     // 如果没有自定义时间间隔,则默认为2秒
       //    self.custom_acceptEventInterval = 2;
       // }
        
        // 是否小于设定的时间间隔
        BOOL needSendAction = (NSDate.date.timeIntervalSince1970 - self.custom_acceptEventTime >= self.custom_acceptEventInterval);
        
        // 更新上一次点击时间戳
        if (self.custom_acceptEventInterval > 0) {
            self.custom_acceptEventTime = NSDate.date.timeIntervalSince1970;
        }
        
        // 两次点击的时间间隔小于设定的时间间隔时,才执行响应事件
        if (needSendAction) {
            [self custom_sendAction:action to:target forEvent:event];
        }
        
        
    }
    @end
    
    

    扩展阅读:

    Objective-C Runtime Reference - Apple Developer
    Objective C运行时(runtime)技术的几个要点总结
    OC运行时编程指南
    Method Swizzling

    相关文章

      网友评论

      • 4e68f4678acb:这样写会有问题的,按钮点击一次 - (void)custom_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event 会响应三次
        当快速点击一个按钮后 等待一会再点击这个按钮,这个按钮可能会不响应
      • YummyDog: // 更新上一次点击时间戳
        if (self.custom_acceptEventInterval > 0) {
        self.custom_acceptEventTime = NSDate.date.timeIntervalSince1970;
        }
        这一段代码我觉得有问题,这种情况下,一直点击按钮,就一直不会触发按钮的点击事件,我把代码改成以下这样
        // 更新上一次点击时间戳
        if (needSendAction) {
        self.custom_acceptEventTime = NSDate.date.timeIntervalSince1970;
        }
        不知道原先的代码是不是作者的本意,如果我妄言了,见谅
        ocarol:需求不同而已
      • Zz7777777:有demo就好了
      • 李某lkb:怎么用呢?
      • Disery:BOOL didAddMethod = class_addMethod(self, sysSEL, method_getImplementation(customMethod), method_getTypeEncoding(customMethod)); 这个sysSEL 是不是该用customSEL
      • 崇德兴仁:自己也测试了下,很好用!多谢楼主分享
      • b2d1df152e2b:1. 不知道" if (didAddMethod) {
        class_replaceMethod(self, customSEL, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
        } " 这段代码有什么意义,可以直接删掉;
        2. 如果button的其他分类中有实现与custom_sendAction:to:forEvent:重名的方法,这个要怎么判断并做过滤呢?
      • NateLam:亲测可用, 非常感谢, 想转载一下做个笔记, 会注明出处的, 再次感谢 :heart:
        ocarol:@NateLam 好的:smile:
      • 十一岁的加重:如果只是解决重复Push,可以用更优的做法
        brownfeng:求指点
        851f2ff53e9c:@十一岁的加重 求教
        ocarol:@十一岁的加重 请不吝赐教:heart_eyes:
      • 这个昵称就很帅:oc 的runtime还是很强大的,mark了
      • Mitchell: 如果直接使用按钮扩展的话,按钮切换状态,会有 bug。
        ocarol:@Mitchell 在分类中不统一设置间隔时间,只在需要点击跳转的按钮上设置间隔时间,没有设置间隔时间的按钮应该不会有影响
      • Ice_tree:简书小白,请教一下你这些代码是如何以这种形式粘贴上去的?
        ocarol:@Ice_tree 是的
        Ice_tree:是用markdown写的吗?
      • 黑白小熊猫:正好项目需要用到这个 :+1:
      • 萧城x:hao

      本文标题:[iOS]利用runtime,解决多次点击相同button,导致

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