有幾個實際業務場景需要控制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]; // } //} - (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
方法中交換了UIControl
的sendAction:to:forEvent:
方法,所以在使用UIControl
或其子類(比如UISlider
)的sendAction:to:forEvent:
方法時會引起參數缺失的崩潰。可以將UIButton+QiEventInterval
改成UIControl+QiEventInterval
以避免此問題。
可從Github獲取工程源碼
關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)
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
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。