问题
通过runtime的swizzling特性实现button重复点击限制,网上相关内容比较多。
但是都是千篇一律的代码,在UIButton的分类category,交换sendAction:to:forEvent:方法,
如:
网络上通常实现方式
这种实现有什么问题呢?
如下图:
当代码内有textfield且实现了输入监听,在输入文本过程中会因为找不到方法而崩溃
textfield输入时候 崩溃
原因及解决方案
原因
如下图,sendAction:to:forEvent:是UIControl的方法,我们替换了该方法实现,其他使用到该方法的类也会走到被替换的方法内,但是此时我们的替换方法是在UIButton的category内定义的,其他类就找不到这个替换方法,所以发生崩溃!
UIControl类.png
解决方案
我们知道sendAction:to:forEvent:是UIControl的方法,且UIButton和UITextfield等使用到sendAction:to:forEvent:方法的类都是继承自UIControl,那可不可以在UIControl的分类去交换sendAction:to:forEvent:实现呢?内部通过类型判断[self isKindOfClass:[UIButton class]]才处理,这样上面找不sendAction:to:forEvent:替换方法的问题就解决了!顺着这思路我们往下看代码实现:
在UIControl的category内:
swizzling核心代码
+(void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSel = @selector(sendAction:to:forEvent:);
SEL swizzlingSel = @selector(em_sendAction:to:forEvent:);
Method originalMethod = class_getInstanceMethod(class, originalSel);
Method swizzlingMethod = class_getInstanceMethod(class, swizzlingSel);
/*添加原有方法originalSel,如果添加成功则说明该类没有原有方法originalSel,是继承自己父类的
*实现原有方法的imp,通过class_replaceMethod替换swizzlingSel实现为originalMethod,这时originalSel-->swizzlingMethod的IMP,swizzlingSel-->originalMethod的IMP
*/
BOOL isAddMethod = class_addMethod(class, originalSel, method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod));
if (isAddMethod) {
class_replaceMethod(class, swizzlingSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, swizzlingMethod);
}
});
}
点击限制,防止快速重复点击核心代码:
-(void)em_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
if ([self isKindOfClass:[UIButton class]]) {//wct20190925 textfield输入时候崩溃添加判断,只管理button的点击事件
if (self.isNeedDelayClick) {//需要延迟点击
if (self.timeInteralOfClickButton <= 0) {//没设置时间间隔,默认为0.4秒
self.timeInteralOfClickButton = 0.4;
}
//当前时间减上次点击时间,间隔大于规定时间间隔,button可点击
BOOL isCanAction = NSDate.date.timeIntervalSince1970 - self.timeInteralEventLastTime >= self.timeInteralOfClickButton;
if (self.timeInteralOfClickButton > 0) {//更新当前点击时间
self.timeInteralEventLastTime = NSDate.date.timeIntervalSince1970;
}
if (isCanAction) {
[self em_sendAction:action to:target forEvent:event];
}
}else{
[self em_sendAction:action to:target forEvent:event];
}
}else{
[self em_sendAction:action to:target forEvent:event];
}
}
通过这种方式实现,我们避免了其他涉及到sendAction:to:forEvent:引发的崩溃,代码更健壮,完美解决了button重复点击的问题。get到的朋友点个❤️
网友评论