一、背景:
在項目開發時遇到一個問題,我在UIScrollerView中添加了一個UISlider的組件,在手勢滑動的過程中,很難滑動到UISlider這個控件,經常是滑動的時候UIScrollerView進行了滾動,而UISlider這個控件沒有滑動,讓人很抓狂。
二、分析
網上說的通過重載UISlider的
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value方法,改變滑塊的響應區域,其實并沒什么卵用。
真正的問題還是出在了event響應上。
我們從先探究UIScrollView的工作原理:
UIScrollView有一個BOOL類型的tracking屬性,用來返回用戶是否已經觸及內容并打算開始滾動,
當手指觸摸到UIScrollView內容的一瞬間,會產生下面的動作:
列表內容
攔截觸摸事件,tracking屬性變為YES
一個內置的計時器開始生效,用來監控在極短的事件間隔內是否發生了手指移動
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;
}