iOS7/8 UIButton高亮狀態延遲問題全解,ios7uibutton
估計很多碼友都遇到過這樣的情況:
UIButton在某些情況下不能立刻響應TouchDown事件,換句話說,迅速點擊按鈕時,你是永遠看不見這個按鈕的高亮狀態的。
而你會發現,出現這種情況時,這些按鈕都在UIScrollView(UITableView)上。
為此我用了一下午時間查貼整理,得到了完美的解決方案。
在介紹解決方案前,必須先科普一些事實,協助大家理解:
UIScrollView:
1、屬性delaysContentTouches,布爾類型,預設值為YES。值為YES時,UIScrollView會在接收到手勢時延遲150ms來判斷該手勢是否能夠出發UIScrollView的滑動事件;值為NO時,UIScrollView會立馬將接收到的手勢分發到子視圖上。
(註:僅僅設定這個是不夠的,你會發現如果想要拖動scrollView而起點落在其他有手勢識別的視圖上時會拖不動)
2、方法- (BOOL)touchesShouldCancelInContentView:(UIView *)view,此方法的重載是協助我們完美解決問題的重點,決定手勢是否取消傳遞到view上,拖動ScrollView時觸發。返回NO時,拖動手勢將留在ScrollView上,返回YES則將手勢傳到view上。(若view是UIControl,則預設返回YES)
UITableView:
不得不說,UITableView(包括UITableViewCell在內)在iOS7和iOS8中的視圖結構是不同的,且存在著很多我們在編碼時永遠接觸不到的視圖,但我們可通過Debug將其subviews逐個逐個找出來。這關係到我們這個問題坑比較深的層次。
iOS7:UITableView中存在n+1個UIScrollView,一個是UITableView本身,另外n個存在於UITableViewCell與cell的contentView之間,類名為UITableViewCellScrollView,活的不久,僅存在於iOS7中,在iOS8中已被移除。
iOS8:UITableView中存在2個UIScrollView,一個是UITableView本身,另外一個存在於UITableView與UITableViewCell之間,類名為UITableViewWrapperView。需要注意的是,UITableViewWrapperView在iOS7中並不是一個UIScrollView。
科普知識完,那麼我們就有了以下的問題解決方案了:
1、將UIButton所有屬於UIScrollView的父視圖的delaysContentTouches屬性設定成為NO。
2、繼承UIScrollView或UITableView,並重寫- (BOOL)touchesShouldCancelInContentView:(UIView*)view方法,讓其響應拖動方法。
以下是參考代碼:
為了簡便我將兩個類的子類寫在同一個檔案中
NoDelayButtonScrollView.h:
#import <UIKit/UIKit.h>@interface NoDelayButtonScrollView : UIScrollView@end@interface NoDelayButtonTableView : UITableView@end
NoDelayButtonScrollView.m(1):
#import "NoDelayButtonScrollView.h"@implementation NoDelayButtonScrollView- (id)initWithCoder:(NSCoder *)aDecoder{ self = [super initWithCoder:aDecoder]; if (self) { self.delaysContentTouches = NO; } return self;}- (BOOL)touchesShouldCancelInContentView:(UIView *)view{ if ([view isKindOfClass:[UIButton class]]) { return YES; } return [super touchesShouldCancelInContentView:view];}@end
NoDelayButtonScrollView.m(2):
@implementation NoDelayButtonTableView- (id)initWithCoder:(NSCoder *)aDecoder{ self = [super initWithCoder:aDecoder]; if (self) {self.delaysContentTouches = NO; // iterate over all the UITableView's subviews for (id view in self.subviews) { // looking for a UITableViewWrapperView if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewWrapperView"]) { // this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7 if([view isKindOfClass:[UIScrollView class]]) { // turn OFF delaysContentTouches in the hidden subview UIScrollView *scroll = (UIScrollView *) view; scroll.delaysContentTouches = NO; } break; } } } return self;}- (BOOL)touchesShouldCancelInContentView:(UIView *)view{ if ([view isKindOfClass:[UIButton class]]) { return YES; } return [super touchesShouldCancelInContentView:view];}@end
以上分別對UIScrollView和UITableView進行繼承,重寫initWithCoder:方法可保證使用Nib檔案也能生效
使用這兩個類繼承寫出來的ScrollView和TableView都能快速響應子Button的TouchDown事件,並顯示高亮
但以上代碼仍未能解決iOS7下UITableView的子Button高亮延遲問題。
可加入以下代碼來解決:
for (id obj in cell.subviews){ if ([NSStringFromClass([obj class]) isEqualToString:@"UITableViewCellScrollView"]) { UIScrollView *scroll = (UIScrollView *) obj; scroll.delaysContentTouches = NO; break; }}
這段代碼可加在Custom的UITableViewCell的initWithCoder:方法中,也可以放在UITableViewDelegate的cellForRowAtIndexPath:方法中設定對應cell中的UITableViewCellScrollView。
以上,是所有協助你解決Button延遲高亮問題的方法。