OC-runtime-防止按鈕多次點擊

按鈕重復(fù)點擊的情況主要有兩種,一種是用戶主動點擊,這個無法避免;二是網(wǎng)絡(luò)耗時造成的延遲情況,促使用戶重復(fù)點擊。


  • 場景一,用戶主動點擊,例如:重復(fù)的點贊和取消點贊的行為。

這種情況如果不進行處理,會造成頻繁訪問服務(wù)器的行為,造成服務(wù)器資源的浪費。

解決方法:對點擊事件進行延遲執(zhí)行,再次點擊時,先取消之前的延遲事件,這樣的話,延遲時間內(nèi)重復(fù)點擊的話,只會執(zhí)行最后一次操作的事件。

   
 // 當(dāng)按鈕再次點擊的時候,取消之前的延遲事件,執(zhí)行新的延遲事件
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(buttonClickedAction:) object:sender];

    [self performSelector:@selector(buttonClickedAction:) withObject:sender afterDelay:1.0];


  • 場景二,網(wǎng)絡(luò)延遲時造成的延遲情況下,用戶再次點擊的行為。

這種情況存在的地方很多,所以進行所有的按鈕全局處理的方案,相對比較理想。

解決方法:使用runtime的方式,自定義方法替換button的事件響應(yīng)方法sendAction,在自定義方法中,設(shè)置響應(yīng)延遲,即是在延遲時間內(nèi)只能響應(yīng)一次事件,這樣等于限制了一定的時間單位內(nèi),只能執(zhí)行一次事件。

點贊等場景,不適合該方式,要單獨處理.

實現(xiàn)方式:給UIButton添加一個分類,然后在分類中動態(tài)添加屬性

/*
 事件延遲時間
 */
@property NSTimeInterval acceptEventInterval;

/*
 事件接收時間
 */
@property NSTimeInterval acceptEventTime;

/*
 是否忽視事件延遲(特殊場景,不要需要延遲)
 */
@property Boolean isIgnoreEventInterval;
在.m中實現(xiàn)getter和setter方法,

#import <objc/message.h>

#define defaultTimeInterval 1.0  // 默認(rèn)延遲1秒

static const char *AcceptEventInterval = "acceptEventInterval";
static const char *AcceptEventTime     = "acceptEventTime";
static const char *IsIgnoreEventInterval = "isIgnoreEventInterval";

@implementation UIButton (Interval)

- (NSTimeInterval)acceptEventInterval{
    
    return [objc_getAssociatedObject(self, &AcceptEventInterval) doubleValue];
}

- (void)setAcceptEventInterval:(NSTimeInterval)acceptEventInterval{
    
    objc_setAssociatedObject(self, &AcceptEventInterval, @(acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSTimeInterval)acceptEventTime{
    
    return [objc_getAssociatedObject(self, &AcceptEventTime) doubleValue];
}

- (void)setAcceptEventTime:(NSTimeInterval)acceptEventTime{
    
    objc_setAssociatedObject(self, &AcceptEventTime, @(acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)setIsIgnoreEventInterval:(Boolean)isIgnoreEventInterval{
    objc_setAssociatedObject(self, &IsIgnoreEventInterval, @(isIgnoreEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (Boolean)isIgnoreEventInterval{
    return [objc_getAssociatedObject(self, &IsIgnoreEventInterval) boolValue];
}

+ (void)load{

   [super load];

    Method fromMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method toMethod = class_getInstanceMethod(self, @selector(xz_sendAction:to:forEvent:));
    
    // 使用自定義的sendAction方法替換系統(tǒng)的sendAction方法
    method_exchangeImplementations(fromMethod, toMethod);
}

- (void)xz_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
    
    // 如果不需要事件延遲,則不進行延遲操作
    if(self.isIgnoreEventInterval == YES){
        
        [self xz_sendAction:action to:target forEvent:event];
        return;
    }
        
    // 這里處理一下,如果沒有達到事件延遲時間,則不執(zhí)行
    NSTimeInterval now = [NSDate date].timeIntervalSince1970;
    
    self.acceptEventInterval = self.acceptEventInterval ? self.acceptEventInterval : defaultTimeInterval;
    
    if (now - self.acceptEventTime < self.acceptEventInterval) {
        return;
    }
    
    if (self.acceptEventInterval > 0) {
        self.acceptEventTime = now;
    }
    
    
    // 因為方法實現(xiàn)已經(jīng)替換,調(diào)用xz_sendAction,就是調(diào)用sendAction的實現(xiàn)方法,所以不會造成死循環(huán)
    [self xz_sendAction:action to:target forEvent:event];
}
使用時,在需要的類中,引入這個頭文件,或者在.pch中引入;
  • 通過按鈕的isIgnoreEventInterval設(shè)置是否忽略按鈕事件延遲;
    btn.isIgnoreEventInterval = YES;
  • 通過按鈕的acceptEventInterval設(shè)置事件延遲的時間。
    btn.acceptEventInterval = 2;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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