公司的內網測試環境由於網絡作過了限制,比較卡,因此測試連續點擊button
或者cell
時可能會屢次push
控制器.如何在代碼改動範圍最小的範圍內來解決這個問題呢?git
程序中大量按鈕沒有作連續響應的校驗,連續點擊出現了不少沒必要要的問題,例如發表帖子操做,用戶手快點擊屢次,就會致使同一帖子發佈屢次。github
#import <UIKit/UIKit.h> //默認時間間隔 #define defaultInterval 1 @interface UIButton (Swizzling) //點擊間隔 @property (nonatomic, assign) NSTimeInterval timeInterval; //用於設置單個按鈕不須要被hook @property (nonatomic, assign) BOOL isIgnore; @end
#import "UIButton+Swizzling.h" #import "NSObject+Swizzling.h" @implementation UIButton (Swizzling) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self methodSwizzlingWithOriginalSelector:@selector(sendAction:to:forEvent:) bySwizzledSelector:@selector(sure_SendAction:to:forEvent:)]; }); } - (NSTimeInterval)timeInterval{ return [objc_getAssociatedObject(self, _cmd) doubleValue]; } - (void)setTimeInterval:(NSTimeInterval)timeInterval{ objc_setAssociatedObject(self, @selector(timeInterval), @(timeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } //當按鈕點擊事件sendAction 時將會執行sure_SendAction - (void)sure_SendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{ if (self.isIgnore) { //不須要被hook [self sure_SendAction:action to:target forEvent:event]; return; } if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) { self.timeInterval =self.timeInterval == 0 ?defaultInterval:self.timeInterval; if (self.isIgnoreEvent){ return; }else if (self.timeInterval > 0){ [self performSelector:@selector(resetState) withObject:nil afterDelay:self.timeInterval]; } } //此處 methodA和methodB方法IMP互換了,實際上執行 sendAction;因此不會死循環 self.isIgnoreEvent = YES; [self sure_SendAction:action to:target forEvent:event]; } //runtime 動態綁定 屬性 - (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{ // 注意BOOL類型 須要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用錯,不然set方法會賦值出錯 objc_setAssociatedObject(self, @selector(isIgnoreEvent), @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)isIgnoreEvent{ //_cmd == @select(isIgnore); 和set方法裏一致 return [objc_getAssociatedObject(self, _cmd) boolValue]; } - (void)setIsIgnore:(BOOL)isIgnore{ // 注意BOOL類型 須要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用錯,不然set方法會賦值出錯 objc_setAssociatedObject(self, @selector(isIgnore), @(isIgnore), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)isIgnore{ //_cmd == @select(isIgnore); 和set方法裏一致 return [objc_getAssociatedObject(self, _cmd) boolValue]; } - (void)resetState{ [self setIsIgnoreEvent:NO]; } @end
使用分類+運行時
來替換Button
的點擊方法,能夠設置一個時間間隔
,點擊事後開啓一個計時器,並關閉按鈕的enable
屬性,計時完成後再打開enable
.至於cell
暫時沒有什麼好點子.網絡
優勢:架構
缺點:併發
通常咱們的網絡請求框架都會封裝
兩到三層AFN
,經過大量的block進行嵌套來完成一系列的請求
工做.因此咱們能夠設置一個全局id
變量,用來記錄當前點擊的button
和cell
,在最底層的網絡請求開始時將這個按鈕/cell的enable
關閉,成功後再次打開.框架
優勢:ide
缺點:測試
咱們能夠控制UINavigationController
中的push
方法,代碼很簡單,只須要判斷當前的控制器和推入的控制器是不是相同的
一個class
就行了.但有一個缺點,若原本就想push
一個相同的控制器就很尷尬了.代碼以下:atom
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { //cell由於網絡請求延遲而屢次push同一頁面 if (![[super topViewController] isKindOfClass:[viewController class]]) { // 若是和上一個控制器同樣,隔絕此操做 [super pushViewController:viewController animated:animated]; } }
連接,這位前輩的方式很巧妙,也解決了我上面的缺點
.spa
override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) { if let navigationController = navigationController { guard navigationController.topViewController == self else { return } } super.performSegueWithIdentifier(identifier, sender: sender) }