OC runtime 机制消除UIButton重复点击问题

来源:互联网 发布:酷乐视q6装软件 编辑:程序博客网 时间:2024/06/04 17:51

问题分析

  用户行为不可控,可能连续点击一个按钮,导致按钮重复事件重复调用。如果能使用润 runtime机制,为按钮添加一个可控的连续点击时间间隔,用于控制按钮点击后,不可点击,再次恢复可点击状态的时间,问题便迎刃而解。所以,我们选择给UIButton添加列表,增加eventTimeInterval属性来解决该问题。

  代码如下:

//UIButton+QYBtnTimer.h#import <UIKit/UIKit.h>@interface UIButton (QYBtnTimer)@property (nonatomic, assign) NSTimeInterval eventTimeInterval;@end//UIButton+QYBtnTimer.m#import "UIButton+QYBtnTimer.h"#import <objc/runtime.h>#define defaultInterval 0.003  //默认时间间隔,不能太长,以免影响系统拍照等功能@interface UIButton ()/** *  bool YES 忽略点击事件   NO 允许点击事件 */@property (nonatomic, assign) BOOL isIgnoreEvent;@end@implementation UIButton (QYBtnTimer)static const char *UIControl_eventTimeInterval = "UIControl_eventTimeInterval";static const char *UIControl_enventIsIgnoreEvent = "UIControl_enventIsIgnoreEvent";+ (void)load{    // Method Swizzling    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        SEL selA = @selector(sendAction:to:forEvent:);        SEL selB = @selector(_wxd_sendAction:to:forEvent:);        Method methodA = class_getInstanceMethod(self,selA);        Method methodB = class_getInstanceMethod(self, selB);        BOOL isAdd = class_addMethod(self, selA, method_getImplementation(methodB), method_getTypeEncoding(methodB));        if (isAdd) {            class_replaceMethod(self, selB, method_getImplementation(methodA), method_getTypeEncoding(methodA));        }else{            //添加失败了 说明本类中有methodB的实现,此时只需要将methodA和methodB的IMP互换一下即可。            method_exchangeImplementations(methodA, methodB);        }    });}- (void)_wxd_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{    self.eventTimeInterval = self.eventTimeInterval == 0 ? defaultInterval : self.eventTimeInterval;    if (self.isIgnoreEvent){        return;    }else if (self.eventTimeInterval > 0){        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.eventTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{            [self setIsIgnoreEvent:NO];        });    }    self.isIgnoreEvent = YES;    // 这里看上去会陷入递归调用死循环,但在运行期此方法是和sendAction:to:forEvent:互换的,相当于执行sendAction:to:forEvent:方法,所以并不会陷入死循环。    [self _wxd_sendAction:action to:target forEvent:event];}// runtime 动态绑定 属性- (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{    objc_setAssociatedObject(self, UIControl_enventIsIgnoreEvent, @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (BOOL)isIgnoreEvent{    return [objc_getAssociatedObject(self, UIControl_enventIsIgnoreEvent) boolValue];}- (NSTimeInterval)eventTimeInterval{    return [objc_getAssociatedObject(self, UIControl_eventTimeInterval) doubleValue];}- (void)setEventTimeInterval:(NSTimeInterval)eventTimeInterval{    objc_setAssociatedObject(self, UIControl_eventTimeInterval, @(eventTimeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);}@end

注意事件

  • 1 defaultInterval 0.003 //默认时间间隔,不能太长,以免影响系统拍照等功能
  • 2 可以将该类别引入pch文件,这样默认的按钮连续点击事件为defaultInterval,如有特殊需求,如点击事件请求数据时间较长,则可以给按钮新属性eventTimeInterval赋值。
原创粉丝点击