【iOS】防止雙十一多次剁手——用runtime解決重復(fù)點(diǎn)擊問(wèn)題

發(fā)現(xiàn)問(wèn)題

雙十一將至,項(xiàng)目上遇到了個(gè)問(wèn)題。用戶(QA??)連續(xù)點(diǎn)擊去下單支付的時(shí)候,微信/支付寶可能會(huì)返回“重復(fù)訂單”的錯(cuò)誤信息(廢話你點(diǎn)這么多次能不重復(fù)么??),同理點(diǎn)擊按鈕多次push的情況也會(huì)出現(xiàn)。

解決思路

這個(gè)問(wèn)題主要因?yàn)閎utton被多次點(diǎn)擊,重復(fù)的響應(yīng)了點(diǎn)擊事件。

解決方案

通常是如何解決

一般做法是在按鈕點(diǎn)擊事件里設(shè)置這個(gè)按鈕不可點(diǎn)擊,適當(dāng)?shù)臅r(shí)間之后再設(shè)置回來(lái),或者在點(diǎn)擊事件最后設(shè)置可以點(diǎn)擊。下面是在響應(yīng)事件的方法內(nèi)禁止點(diǎn)擊的代碼。

- (IBAction)clickBtn1:(UIbutton *)sender
{
    sender.enabled = NO;
    doSomething
    sender.enabled = YES;
}

如果想做N秒內(nèi)禁止點(diǎn)重復(fù)擊操作則需要用方法performSelector:withObject:afterDelay:如果想解決程序里所有重復(fù)點(diǎn)擊的方法,需要把每個(gè)按鈕都寫(xiě)一遍,這不扯淡么…

微笑再見(jiàn).png

漂亮的解決方法

UIbutton繼承自UIControl,可以給UIControl添加Category。
MasterDefaultRepeatEventInterval可以定制時(shí)間間隔。

#import <UIKit/UIKit.h>

static NSTimeInterval MasterDefaultRepeatEventInterval = 1.5;

@interface UIControl (Interval)

@property (nonatomic, assign) NSTimeInterval M_repeatEventInterval;
@end

這里用了單例的方法來(lái)替換。

#import "UIControl+Interval.h"
#import <objc/runtime.h>

@implementation UIControl (Interval)
- (NSTimeInterval)M_repeatEventInterval {
    return [objc_getAssociatedObject(self, _cmd) doubleValue];
}
- (void)setM_repeatEventInterval:(NSTimeInterval)M_repeatEventInterval {
     objc_setAssociatedObject(self, @selector(M_repeatEventInterval), @(M_repeatEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalselector = @selector(sendAction:to:forEvent:);
        SEL swizzledSelector = @selector(M_sendAction:to:forEvent:);
        
        Method originalMethod = class_getInstanceMethod(class, @selector(sendAction:to:forEvent:));
        Method swizzleMethod = class_getInstanceMethod(class, @selector(M_sendAction:to:forEvent:));

        //添加方法 語(yǔ)法:BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) 若添加成功則返回No
        // cls:被添加方法的類  name:被添加方法方法名  imp:被添加方法的實(shí)現(xiàn)函數(shù)  types:被添加方法的實(shí)現(xiàn)函數(shù)的返回值類型和參數(shù)類型的字符串
        BOOL didAddMethod = class_addMethod(class, originalselector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
       
        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }else{
            method_exchangeImplementations(originalMethod, swizzleMethod);
        }
        
        
    });
}

- (void)M_sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event {
    static BOOL ignoreEvent = NO;
    if (self.M_repeatEventInterval > 0) {
        if (ignoreEvent) {
            return;
        }else{
            ignoreEvent = YES;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.M_repeatEventInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                ignoreEvent = NO;
            });
            [self M_sendAction:action to:target forEvent:event];
        }
    }else {
        [self M_sendAction:action to:target forEvent:event];
    }
}

就這樣!很快,很棒,很優(yōu)雅。就這樣吧??


嗨呀,開(kāi)心.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容