在有些開發(fā)場景下,我們在scrollView上放置了視圖,我們想未按住子視圖時拖動scrollView,按住子視圖時拖動子視圖, 但是實際會發(fā)現(xiàn)按住視圖 ,無法拖動視圖,拖動的還是scrollView。
原因是:
手勢識別器比UIResponder具有更高的事件響應優(yōu)先級。
Window在將事件傳遞給hit-tested view之前,會先將事件傳遞給相關的手勢識別器并由手勢識別器優(yōu)先識別,而scrollView上系統(tǒng)就添加了手勢。若手勢識別器成功識別了事件,就會取消hit-tested view對事件的響應;若手勢識別器沒能識別事件,hit-tested view才完全接手事件的響應權(quán)。
解決辦法: 設置delaysContentTouches = NO. 繼承 scrollView 重寫下面方法
called before scrolling begins if touches have already been delivered to a subview of the scroll view. if it returns NO the touches will continue to be delivered to the subview and scrolling will not occur
not called if canCancelContentTouches is NO. default returns YES if view isn't a UIControl
this has no effect on presses
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
if ([view isKindOfClass:NSClassFromString(@"TestView")]) { //判斷是否是需要拖動的子視圖,
return NO;
}
return [super touchesShouldCancelInContentView:view];
}
scrollView.delaysContentTouches = NO;
為什么這樣做?
touchesShouldCancelInContentView解讀:是否取消給定視圖上的touches事件傳遞。
- 該方法會在scrollView滾動前調(diào)用,前提是touches事件已經(jīng)傳遞到子視圖上面, 如果返回NO,那么事件就會繼續(xù)傳遞到子視圖上面,scrollView就不會響應滾動了。
- canCancelContentTouches=YES時該方法才會調(diào)用。canCancelContentTouches:
default is YES. if NO, then once we start tracking, we don't try to drag if the touch moves.canCancelContentTouches設置為NO時,表示拖動視圖時,不再去drag scrollView??唇忉?,感覺我們這個需求直接設置這個參數(shù)為NO即可,經(jīng)過我的測試,確實是可以的,但是需要拖動視圖時,需要按住一段時間后,才能拖動視圖,否則還是會拖動scrollView,這個涉及到手勢的識別過程,長按過程中scrollView的手勢識別完成后,才會將事件交由hit-tested view。所以設置這個參數(shù)為NO是不夠達到所需效果的。 - default returns YES if view isn't a UIControl:在子視圖不是UIControl是默認返回YES, 意思子視圖是UIControl的話,事件會傳遞到UIControl上,另一個方面說明UIControl事件優(yōu)先級更高。
scrollView.delaysContentTouches 解讀:這個字段描述的是子控件響應觸摸事件是否立即響應觸摸事件,默認為YES。
- 該設置應該是一種優(yōu)化,因為系統(tǒng)識別手勢需要一定的時間判斷, 該值默認YES,給手勢識別預留了0.15s的時間,在這個時間內(nèi),hit-tested view對事件的響應就被延遲,如果直接scrollView手勢直接識別了,那么subView的touchBegin都不會執(zhí)行了。
- 如果默認為YES的話,會發(fā)現(xiàn)按住就馬上開始移動手指時還是scrollView在滾動。
這是因為subView的touchBegin都沒執(zhí)行,也就是說touches事件沒有傳遞到子視圖上面touchesShouldCancelInContentView由于條件未達到不會觸發(fā)。 反而要按住一段時間(0.15s),scrollView的拖動手勢沒能識別事件后,touches事件傳遞到subView上,subView的touchesBegin被觸發(fā), 再發(fā)生移動時觸發(fā)touchesShouldCancelInContentView調(diào)用, 最終subView的touchesMove觸發(fā), subView才能拖動。 - 所以設置為YES,讓按下時touches事件就直接傳遞到subView上,移動時直接觸發(fā)重寫的touchesShouldCancelInContentView。
最后
開發(fā)中,響應鏈、手勢、target-action這一系列響應觸摸事件,往往混合在一起,出現(xiàn)的問題讓人頭大,如果不是對iOS觸發(fā)事件十分深入了解,很難很好的處理到問題,以及處理完了也不知道具體為什么,這里推薦這表文章iOS觸摸事件全家桶,這篇文章很深入淺出的講解了iOS觸摸事件的一系列機制。