iOS開發(fā)禁用多個按鈕同時點擊導致誤觸
在開發(fā)iOS項目的時候很多開發(fā)者都不會考慮這樣的一個問題,包括我也不會------一個界面內(nèi)有多個按鈕或者touch事件同時進行會怎么樣?
單從字面上看可能不容易理解,舉個比較容易理解的例子.
在某個應(yīng)用的首頁上,有兩個按鈕,我們單獨的點擊每一個按鈕都會觸發(fā)一次點擊事件,這個本身沒有任何毛病,類似于點擊第一個按鈕跳轉(zhuǎn)到第一個子界面,點擊第二個按鈕跳轉(zhuǎn)到第二個子界面.但是我們是做的移動開發(fā),我們會不會有過這樣的尷尬呢?我們手指比較粗,或者多個手指同事操作, 偶然間的我們會發(fā)生誤觸,導致一個尷尬的場景------兩個按鈕同時點擊了,那么樣的結(jié)果就是,會閃現(xiàn)兩次跳轉(zhuǎn)頁面,即跳轉(zhuǎn)第一個子界面和跳轉(zhuǎn)第二個子界面都會發(fā)生.很顯然這個不是我們想要的.那么怎么屏蔽呢?
1.我們可能會想到禁用按鈕點擊的方式,如果按鈕1點擊了,立刻禁止按鈕2的點擊,事件結(jié)束后開啟按鈕2的點擊,同樣的按鈕2頁做類似的操作,
實驗下來有效么? 不能說完全無效,如果禁止按鈕點擊的代碼已經(jīng)實現(xiàn)了的話.
我們多次嘗試,還是會發(fā)現(xiàn)有誤觸現(xiàn)象.這個方法原理上行得通,但是,真正點擊的時候,你會發(fā)現(xiàn)禁止按鈕點擊的實現(xiàn)和點擊按鈕的事件的先后并不能完全保證...
如果兩次點擊稍微錯開一點點時間差是沒有問題的,但是如果兩次點擊很接近就會出現(xiàn)問題.
如果是這樣的狀態(tài),沒有問題
但是如果時間段上移一點,則一樣沒有效果
當然如果你說你可以采用延遲執(zhí)行的方式,保證每次執(zhí)行都會延遲操作,并且做判斷...當然這樣復雜的操作是肯定可以實現(xiàn)的,但是很顯然太復雜了.
那么我們就沒有辦法了嗎?或者我們找找系統(tǒng)方法,看看有沒有能夠?qū)崿F(xiàn)的其他途徑.
2.很顯然,iOS開發(fā)的框架中是有這樣的方法的.
UIView的UIViewGeometry分類中有這樣的一個屬性.
很顯然,不是遇到這種特殊情況需求的開發(fā)者是很難去有機會了解這個屬性的.
這個屬性很簡單,從翻譯上看:獨家接觸 那么它就一個作用,防止誤觸(同時點擊).當然,它默認是NO,所以我們在開發(fā)中會有誤觸發(fā)生,只要我們設(shè)置為YES即可..
那么我們在開發(fā)中就會多寫一行代碼即可,在創(chuàng)建的時候. 類似于[button setExclusiveTouch:YES]; 或者button.exclusiveTouch = YES;
是的,你已經(jīng)嘗試到了結(jié)果,正是我們想要的,我們不會再發(fā)生那種尷尬的非邏輯上的錯誤發(fā)生了,不懂代碼的老板在拿著你的應(yīng)用最起碼不會因為這個問題而說你不會開發(fā)了(回想剛剛開發(fā)的時候的我.....).
3.有了這么好的方法,我們能夠加以利用呢?很顯然我們在開發(fā)中基本上是不會有同時觸碰的事件居多的,真正需要同時觸碰的基本上應(yīng)該是沒有的(游戲除外...)
我們也都有了解iOS開發(fā)中的runtime機制.我們能否利用這個,將這行代碼給直接寫好呢?
我想到了一個方法.所有的UIView添加到界面上很顯然都會需要這個方法,addSubview:
于是我就想到了創(chuàng)建一個分類,應(yīng)該會好點
#import "UIView+Extension.h"
#import <objc/runtime.h>
@implementation UIView (Extension)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL systemSel = @selector(addSubview:);
SEL swizzSel = @selector(myAddSubview:);
Method systemMethod = class_getInstanceMethod([self class], systemSel);
Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (isAdd) {
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
}else{
method_exchangeImplementations(systemMethod, swizzMethod);
}
});
}
- (void)myAddSubview:(UIView *)view{
if ([view respondsToSelector:@selector(setExclusiveTouch:)]) {
[view setExclusiveTouch:YES];
}
[self myAddSubview:view];
}
@end
采用runtime的機制,讓view在被擁有者添加到視圖上的之前先設(shè)置exclusiveTouch屬性.
很顯然,以后我們在寫代碼的時候再也不需要關(guān)心這個了,所有的按鈕,所有的繼承或者來自UIView的都會默認實現(xiàn),我們回顧一下,我們所有能觸發(fā)點擊的控件,有哪一個不是繼承自UIView的呢?
于是乎,我們完美的禁用了多個按鈕同時點擊導致誤觸的尷尬發(fā)生.
3.當然上面的邏輯只是我的一個思路,我們也可以采用遍歷的方式啊
我們替換VC中viewDidAppear:的方法
#import "UIViewController+Extension.h"
@implementation UIViewController (Extension)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewDidAppear:);
SEL swizzledSelector = @selector(myViewDidAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (success) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
-(void)myViewDidAppear:(BOOL)animated{
for (UIView * subview in self.view.subviews) {
if ([subview respondsToSelector:@selector(setExclusiveTouch:)]) {
[subview setExclusiveTouch:YES];
}
}
[self myViewDidAppear:animated];
}
@end
在viewController顯示到屏幕的時候在viewDidAppear:中遍歷子視圖,當然子視圖中是否有子視圖?這個是否也需要遍歷?這里也是需要開發(fā)者自己考慮的,是深度遍歷,還是廣度遍歷...這些都不在本博客討論范圍內(nèi)...