1. 程式人生 > >【技巧】UIScrollerView中的UISlider優先響應

【技巧】UIScrollerView中的UISlider優先響應

一、背景:

在專案開發時遇到一個問題,我在UIScrollerView中添加了一個UISlider的元件,在手勢滑動的過程中,很難滑動到UISlider這個控制元件,經常是滑動的時候UIScrollerView進行了滾動,而UISlider這個控制元件沒有滑動,讓人很抓狂。

二、分析

網上說的通過過載UISlider的
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value方法,改變滑塊的響應區域,其實並沒什麼卵用。
真正的問題還是出在了event響應上。

我們從先探究UIScrollView的工作原理:
UIScrollView有一個BOOL型別的tracking屬性,用來返回使用者是否已經觸及內容並打算開始滾動,
當手指觸控到UIScrollView內容的一瞬間,會產生下面的動作:

  1. 列表內容
  2. 攔截觸控事件,tracking屬性變為YES
  3. 一個內建的計時器開始生效,用來監控在極短的事件間隔內是否發生了手指移動

case1:當檢測到時間間隔內手指發生了移動,UIScrollView自己觸發滾動,tracking屬性變為NO,手指觸控下即使有(可以響應觸控事件的)內部控制元件也不會再響應觸控事件。

case2:當檢測到時間間隔內手指沒有移動,tracking屬性保持YES,手指觸控下如果有(可以響應觸控事件的)內部控制元件,則將觸控事件傳遞給控制元件進行處理。

總結:當手指touch的時候,UIScrollView會攔截所有event,然後等待150ms,在這段時間內,如果沒有手指沒有移動,當時間結束時,UIScrollView會發送tracking event到子檢視上,並且自身不滑動。在時間結束前,手指發生了移動,那麼UIScrollView就會進行滑動,從而取消傳送tracking。
對於我們現在的情況,就是
直接拖動UISlider,此時touch時間在150ms以內,UIScrollView會認為是拖動自己,從而攔截了event,導致UISlider接受不到滑動的event。但是隻要按住UISlider一會再拖動,此時touch時間超過150ms,因此滑動的event會發送到UISlider上

三、解決

重寫UIScrollView的hitTest方法:當滑動UISlider時,使UIScrollView不可滑動

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    /*
     直接拖動UISlider,此時touch時間在150ms以內,UIScrollView會認為是拖動自己,從而攔截了event,導致UISlider接受不到滑動的event。但是隻要按住UISlider一會再拖動,此時此時touch時間超過150ms,因此滑動的event會發送到UISlider上。
     */
    UIView *view = [super hitTest:point withEvent:event];
    if([view isKindOfClass:[UISlider class]]) {
        //如果響應view是UISlider,則scrollview禁止滑動
        self.scrollEnabled = NO;
    } else {   //如果不是,則恢復滑動
        self.scrollEnabled = YES;
    }
    return view;
}