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