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


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

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

  • 方案 1:通過UIButtonenabled屬性和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的類別,使用runtimeUIButton增加public屬性qi_eventIntervalprivate屬性eventUnavailable
    2、在+load方法中使用runtimeUIButton-sendAction:to:forEvent:方法與自定義的-qi_sendAction:to:forEvent:方法交換Implementation
    3、使用qi_eventInterval作為控制eventUnavailable的計時因子,用eventUnavailable開控制UIButtonevent事件是否有效。

方案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]; // } //} - (void)qi_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { if([self isMemberOfClass:[UIButton class]]) { if (self.eventUnavailable == NO) { self.eventUnavailable = YES; [self qi_sendAction:action to:target forEvent:event]; [self performSelector:@selector(setEventUnavailable:) withObject:0 afterDelay:self.qi_eventInterval]; } } else { [self qi_sendAction:action to:target forEvent:event]; } } #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秒 

PS:針對方案3,因為在UIButton+QiEventInterval.m中的+load方法中交換了UIControlsendAction:to:forEvent:方法,所以在使用UIControl或其子類(比如UISlider)的sendAction:to:forEvent:方法時會引起參數缺失的崩潰。可以將UIButton+QiEventInterval改成UIControl+QiEventInterval以避免此問題。

可從Github獲取工程源碼


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

推薦文章:iOS UIButton之UIEdgeInsets詳解

iOS 計算時間差   避免過度使用定時器

  (2017-12-13 19:49:46)
   
代碼繁瑣,先做筆記,有時間進行優化
 

 NSString *recordDate = [[NSUserDefaultsstandardUserDefaults]objectForKey:@"recordDate"];

    

    if (recordDate.length != 0) {

    

        NSDate *nowDate = [NSDate date]; // 當前時間

    

        NSDateFormatter *formatter = [[NSDateFormatter alloc]init];

        

        [formatter setDateFormat:@"yyyy/MM/dd HH:mm:ss:SSS"];

        

        NSString*timeString=[formatter stringFromDate: nowDate];

      

        double timeDiff = 0.0;

        

        NSDateFormatter *formatters = [[NSDateFormatter alloc]init];

        [formatters setDateFormat:@"yyyy/MM/dd HH:mm:ss:SSS"];

        NSDate *dateS = [formatters dateFromString:recordDate];

        

        NSDateFormatter *formatterE = [[NSDateFormatter alloc]init];

        [formatterE setDateFormat:@"yyyy/MM/dd HH:mm:ss:SSS"];

        NSDate *dateE = [formatterE dateFromString:timeString];

        

        timeDiff = [dateE timeIntervalSinceDate:dateS ];

        int myInt = (int)timeDiff;

        if (myInt < 20) {

           

            MBProgressHUD *hud = [[MBProgressHUD alloc] init];

            [self.view addSubview:hud];

            hud.labelText = @"請勿頻繁操作";//@"網絡連接失敗";

            hud.mode = MBProgressHUDModeText;

            [hud showAnimated:YES whileExecutingBlock:^{

                sleep(1);

            } completionBlock:^{

                [hud removeFromSuperview];

            }];

            return;

           

        }

  

    }

 

 

 

#pragma mark-保存本地時間到沙盒

-(void)currentDateString{

    

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

    

    [formatter setDateFormat:@"yyyy/MM/dd HH:mm:ss:SSS"];

    

    NSString*timeString=[formatter stringFromDate: [NSDate date]];

    

//    NSDate *currentDate = [NSDate date];

//    NSString *currentDateString = [NSString stringWithFormat:@"%ld", (long)[currentDate timeIntervalSince1970]];

    [[NSUserDefaults standardUserDefaults] setObject:timeStringforKey:@"recordDate"];

    [[NSUserDefaults standardUserDefaults] synchronize];

 

}

 

作者:QiShare
鏈接:https://www.jianshu.com/p/c2243ac4f620
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM