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

问题分析

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

  代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//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
</objc></uikit>

注意事件

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

猜你喜欢

转载自blog.csdn.net/wujakf/article/details/80178493
今日推荐