問題
通過runtime的swizzling特性實(shí)現(xiàn)button重復(fù)點(diǎn)擊限制驶沼,網(wǎng)上相關(guān)內(nèi)容比較多。
但是都是千篇一律的代碼争群,在UIButton的分類category回怜,交換sendAction:to:forEvent:方法,
如:
這種實(shí)現(xiàn)有什么問題呢换薄?
如下圖:
當(dāng)代碼內(nèi)有textfield且實(shí)現(xiàn)了輸入監(jiān)聽玉雾,在輸入文本過程中會(huì)因?yàn)檎也坏椒椒ǘ罎?/p>
原因及解決方案
原因
如下圖,sendAction:to:forEvent:是UIControl的方法轻要,我們替換了該方法實(shí)現(xiàn)复旬,其他使用到該方法的類也會(huì)走到被替換的方法內(nèi),但是此時(shí)我們的替換方法是在UIButton的category內(nèi)定義的冲泥,其他類就找不到這個(gè)替換方法驹碍,所以發(fā)生崩潰壁涎!
解決方案
我們知道sendAction:to:forEvent:是UIControl的方法,且UIButton和UITextfield等使用到sendAction:to:forEvent:方法的類都是繼承自UIControl志秃,那可不可以在UIControl的分類去交換sendAction:to:forEvent:實(shí)現(xiàn)呢怔球?內(nèi)部通過類型判斷[self isKindOfClass:[UIButton class]]才處理,這樣上面找不sendAction:to:forEvent:替換方法的問題就解決了浮还!順著這思路我們往下看代碼實(shí)現(xiàn):
在UIControl的category內(nèi):
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,是繼承自己父類的
*實(shí)現(xiàn)原有方法的imp,通過class_replaceMethod替換swizzlingSel實(shí)現(xiàn)為originalMethod,這時(shí)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);
}
});
}
點(diǎn)擊限制竟坛,防止快速重復(fù)點(diǎn)擊核心代碼:
-(void)em_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
if ([self isKindOfClass:[UIButton class]]) {//wct20190925 textfield輸入時(shí)候崩潰添加判斷,只管理button的點(diǎn)擊事件
if (self.isNeedDelayClick) {//需要延遲點(diǎn)擊
if (self.timeInteralOfClickButton <= 0) {//沒設(shè)置時(shí)間間隔钧舌,默認(rèn)為0.4秒
self.timeInteralOfClickButton = 0.4;
}
//當(dāng)前時(shí)間減上次點(diǎn)擊時(shí)間流码,間隔大于規(guī)定時(shí)間間隔,button可點(diǎn)擊
BOOL isCanAction = NSDate.date.timeIntervalSince1970 - self.timeInteralEventLastTime >= self.timeInteralOfClickButton;
if (self.timeInteralOfClickButton > 0) {//更新當(dāng)前點(diǎn)擊時(shí)間
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];
}
}
通過這種方式實(shí)現(xiàn)延刘,我們避免了其他涉及到sendAction:to:forEvent:引發(fā)的崩潰,代碼更健壯六敬,完美解決了button重復(fù)點(diǎn)擊的問題碘赖。get到的朋友點(diǎn)個(gè)??