[iOS] 利用 runtime,解决多次点击相同 button,导致重复跳转的问题

ytd069qxa 5年前
   <h2>场景</h2>    <p>当app有点卡的时候,多次点击相同的button,经常出现,跳转了N次相同的界面(比如闲鱼)</p>    <h2>解决办法</h2>    <p>用运行时和分类,替换 UIControl 响应事件,根据响应的间隔时间来判断是否执行事件。</p>    <h2>详细步骤</h2>    <ol>     <li>UIControl <p>创建一个 UIControl 的分类</p> <img src="https://simg.open-open.com/show/37e620efadfd5499861e529ac509fc4e.png"> <p>Snip20160816_3.png</p> </li>    </ol>    <p><img src="https://simg.open-open.com/show/71931c467ca5c9ed054e488d250a20bb.png"></p>    <p>Snip20160816_4.png</p>    <p>为了方便他人调整不同的间隔时间需求,在 UIControl+Custom.h 文件中开放间隔时间属性, UIControl+Custom.h 文件的代码为:</p>    <pre>  <code class="language-swift">//  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</code></pre>    <p>在 UIControl+Custom.m 文件中实现方法交换(妥善的做法是:先添加方法,如果方法已经存在,就替换原方法),在 UIControl+Custom.m 文件的代码为:</p>    <pre>  <code class="language-swift">//  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</code></pre>    <h3>扩展阅读:</h3>    <p><a href="/misc/goto?guid=4959677036712763336" rel="nofollow,noindex">Objective-C Runtime Reference - Apple Developer</a></p>    <p><a href="/misc/goto?guid=4959677036793623866" rel="nofollow,noindex">Objective C运行时(runtime)技术的几个要点总结</a></p>    <p><a href="/misc/goto?guid=4959677036875187984" rel="nofollow,noindex">OC运行时编程指南</a></p>    <p><a href="/misc/goto?guid=4958988058459808734" rel="nofollow,noindex">Method Swizzling</a></p>    <p> </p>    <p>来自:http://www.jianshu.com/p/65ce6471cd0f</p>    <p> </p>    <p><span style="background:rgb(189, 8, 28) url("data:image/svg+xml; border-radius:2px; border:medium none; color:rgb(255, 255, 255); cursor:pointer; display:none; font:bold 11px/20px "Helvetica Neue",Helvetica,sans-serif; left:70px; opacity:0.85; padding:0px 4px 0px 0px; position:absolute; text-align:center; text-indent:20px; top:361px; width:auto; z-index:8675309">Save</span></p>