问题分析
用户行为不可控,可能连续点击一个按钮,导致按钮重复事件重复调用。如果能使用润 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>
|