1. 程式人生 > >iOS7/8 UIButton高亮狀態延遲有關問題全解

iOS7/8 UIButton高亮狀態延遲有關問題全解

iOS7/8 UIButton高亮狀態延遲問題全解

估計很多碼友都遇到過這樣的情況:

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:

[objc]  view plain  copy
  1. #import <UIKit/UIKit.h>  
  2. @interface NoDelayButtonScrollView : UIScrollView  
  3. @end  
  4.   
  5. @interface NoDelayButtonTableView : UITableView  
  6. @end  

NoDelayButtonScrollView.m(1):

[objc]  view plain  copy
  1. #import "NoDelayButtonScrollView.h"  
  2. @implementation NoDelayButtonScrollView  
  3. - (id)initWithCoder:(NSCoder *)aDecoder  
  4. {  
  5.     self = [super initWithCoder:aDecoder];  
  6.     if (self)  
  7.     {  
  8.        self.delaysContentTouches = NO;  
  9.     }  
  10.     return self;  
  11. }  
  12. - (BOOL)touchesShouldCancelInContentView:(UIView *)view  
  13. {  
  14.     if ([view isKindOfClass:[UIButton class]])  
  15.     {  
  16.         return YES;  
  17.     }  
  18.     return [super touchesShouldCancelInContentView:view];  
  19. }  
  20. @end  

NoDelayButtonScrollView.m(2):

[objc]  view plain  copy
  1. @implementation NoDelayButtonTableView  
  2. - (id)initWithCoder:(NSCoder *)aDecoder  
  3. {  
  4.     self = [super initWithCoder:aDecoder];  
  5.     if (self)  
  6.     {  
  7. self.delaysContentTouches = NO;  
  8.   
  9.         // iterate over all the UITableView's subviews  
  10.         for (id view in self.subviews)  
  11.         {  
  12.             // looking for a UITableViewWrapperView  
  13.             if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewWrapperView"])  
  14.             {  
  15.                 // this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7  
  16.                 if([view isKindOfClass:[UIScrollView class]])  
  17.                 {  
  18.                     // turn OFF delaysContentTouches in the hidden subview  
  19.                     UIScrollView *scroll = (UIScrollView *) view;  
  20.                     scroll.delaysContentTouches = NO;  
  21.                 }  
  22.                 break;  
  23.             }  
  24.         }  
  25.     }  
  26.     return self;  
  27. }  
  28.   
  29.   
  30. - (BOOL)touchesShouldCancelInContentView:(UIView *)view  
  31. {  
  32.     if ([view isKindOfClass:[UIButton class]])  
  33.     {  
  34.         return YES;  
  35.     }  
  36.     return [super touchesShouldCancelInContentView:view];  
  37. }  
  38. @end  

以上分別對UIScrollView和UITableView進行繼承,重寫initWithCoder:方法可保證使用Nib檔案也能生效
使用這兩個類繼承寫出來的ScrollView和TableView都能快速響應子Button的TouchDown事件,並顯示高亮
但以上程式碼仍未能解決iOS7下UITableView的子Button高亮延遲問題。


可加入以下程式碼來解決:

[objc]  view plain  copy
  1. for (id obj in cell.subviews)  
  2. {  
  3.     if ([NSStringFromClass([obj class]) isEqualToString:@"UITableViewCellScrollView"])  
  4.     {  
  5.         UIScrollView *scroll = (UIScrollView *) obj;  
  6.         scroll.delaysContentTouches = NO;  
  7.         break;  
  8.     }  
  9. }  

這段程式碼可加在Custom的UITableViewCell的initWithCoder:方法中,也可以放在UITableViewDelegate的cellForRowAtIndexPath:方法中設定對應cell中的UITableViewCellScrollView。


以上,是所有幫助你解決Button延遲高亮問題的方法。