iOS UIButton之防止重複點選(控制事件響應時間間隔)

QiShare發表於2018-08-09

級別:★★☆☆☆
標籤:「UIButton Runtime」「UIButton點選頻率」「UIButton防止多次點選」
作者: Xs·H

有幾個實際業務場景需要控制UIButton響應事件的時間間隔。比如:
1、當通過點選按鈕來執行網路請求時,若請求耗時稍長,使用者往往會再點一次。這樣,就執行了兩次請求,造成了資源浪費。
2、在移動終端效能較差時(比如iPhone 6升級到iOS 11?),連續點選按鈕會執行多次事件(比如push出來多個viewController)。
3、防止暴力點選。

控制按鈕響應事件時間間隔的方案不止一種。比如:

  • 方案 1:通過UIButton的enabled屬性和userInteractionEnabled屬性控制按鈕是否可點選。此方案在邏輯上比較清晰、易懂,但具體程式碼書寫分散,常常涉及多個方法。
- (void)buttonClicked:(UIButton *)sender {
    
    sender.enabled = NO;
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        sender.enabled = YES;
    });
}
複製程式碼
  • 方案2:通過NSObject的+cancelPreviousPerformRequestsWithTarget:selector:object:方法和-performSelector:withObject:afterDelay:方法控制按鈕的響應事件的執行時間間隔。此方案會在連續點選按鈕時取消之前的點選事件,從而只執行最後一次點選事件,會出現延遲現象。
- (void)buttonClicked:(UIButton *)sender {
    
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(buttonClickedAction:) object:sender];
    [self performSelector:@selector(buttonClickedAction:) withObject:sender afterDelay:2.0];
}
複製程式碼

在需要對大量UIButton做控制的場景中,方案1和方案2會比較不方便。針對此場景,著重說一下方案3。

  • 方案3:通過Runtime控制UIButton響應事件的時間間隔。思路如下:
    1、建立一個UIButton的類別,使用runtime為UIButton增加public屬性qi_eventInterval和private屬性eventUnavailable。
    2、在+load方法中使用runtime將UIButton的-sendAction:to:forEvent:方法與自定義的-qi_sendAction:to:forEvent:方法交換Implementation。
    3、使用qi_eventInterval作為控制eventUnavailable的計時因子,用eventUnavailable開控制UIButton的event事件是否有效。

方案3可以對所有UIButton生效,具體實現程式碼如下:

@interface UIButton (QiEventInterval)

@property (nonatomic, assign) NSTimeInterval qi_eventInterval;

@end
複製程式碼
#import "UIButton+QiEventInterval.h"
#import <objc/runtime.h>

static char * const qi_eventIntervalKey = "qi_eventIntervalKey";
static char * const eventUnavailableKey = "eventUnavailableKey";

@interface UIButton ()

@property (nonatomic, assign) BOOL eventUnavailable;

@end

@implementation UIButton (QiEventInterval)

+ (void)load {
    
    Method method = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method qi_method = class_getInstanceMethod(self, @selector(qi_sendAction:to:forEvent:));
    method_exchangeImplementations(method, qi_method);
}


#pragma mark - Action functions

- (void)qi_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
    
    if (self.eventUnavailable == NO) {
        self.eventUnavailable = YES;
        [self qi_sendAction:action to:target forEvent:event];
        [self performSelector:@selector(setEventUnavailable:) withObject:@(NO) afterDelay:self.qi_eventInterval];
    }
}


#pragma mark - Setter & Getter functions

- (NSTimeInterval)qi_eventInterval {
    
    return [objc_getAssociatedObject(self, qi_eventIntervalKey) doubleValue];
}

- (void)setQi_eventInterval:(NSTimeInterval)qi_eventInterval {
    
    objc_setAssociatedObject(self, qi_eventIntervalKey, @(qi_eventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)eventUnavailable {
    
    return [objc_getAssociatedObject(self, eventUnavailableKey) boolValue];
}

- (void)setEventUnavailable:(BOOL)eventUnavailable {
    
    objc_setAssociatedObject(self, eventUnavailableKey, @(eventUnavailable), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
複製程式碼


使用方法:

UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
/* here is some button's configuration codes */
[self.view addSubview:button];
    
//! 設定按鈕的點選響應間隔時間
button.qi_eventInterval = 2.0;
複製程式碼


效果展示:

  • 預設Button點選效果:
    不設定qi_eventInterval (預設為0)
  • 設定qi_eventInterval為2秒:
    設定qi_eventInterval為2秒

可從Github獲取工程原始碼

關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)

推薦文章:iOS UIButton之UIEdgeInsets詳解

相關文章