ScrollView內(nèi)嵌SubView時(shí)手勢(shì)處理原理

在實(shí)現(xiàn)上一篇介紹的自定義滑動(dòng)關(guān)聯(lián)菜單控件BFScrollMenu時(shí),關(guān)于滑動(dòng)方向判斷的邏輯其實(shí)一開始是準(zhǔn)備用手勢(shì)操作來實(shí)現(xiàn)的习柠,結(jié)果發(fā)現(xiàn)在ScrollView中處理手勢(shì)的邏輯比較困難能真,在寫的時(shí)候還沒有仔細(xì)研究過ScrollView的滑動(dòng)原理稿湿,只是知道需要自定義一個(gè)ScrollView才能實(shí)現(xiàn)辫愉,但是我的本意是不希望用戶還要顯示指定一個(gè)自定義的ScrollView而是直接用category進(jìn)行無縫的對(duì)接顽决,所以就改用didScroll delegate 用offset來計(jì)算滑動(dòng)邏輯了窍仰,效果可以接受汉规。

回過頭來花了些時(shí)間仔細(xì)研究了下UIScrollView的滑動(dòng)處理邏輯,這樣就可以采用Customer ScrollView來實(shí)現(xiàn)同樣的BFScrollMenu邏輯了驹吮。

  • UIScrollView的滑動(dòng)處理原理

網(wǎng)上相關(guān)的文章有很多针史,但是我感覺沒有一個(gè)能清晰的解釋清楚。這里我用流程圖的方式碟狞,把我自己經(jīng)過試驗(yàn)后的結(jié)論和理解和大家分享一下啄枕,希望能幫助到你。如果有不正確的歡迎指正族沃。

首先我們來看Apple的官方代碼文檔里的注釋:

Scrolling with no scroll bars is a bit complex. on touch down, we don't know if the user will want to scroll or track a subview like a control. on touch down, we start a timer and also look at any movement. if the time elapses without sufficient change in position, we start sending events to the hit view in the content subview. if the user then drags far enough, we switch back to dragging and cancel any tracking in the subview. the methods below are called by the scroll view and give subclasses override points to add in custom behaviour. you can remove the delay in delivery of touchesBegan:withEvent: to subviews by setting delaysContentTouches to NO.

然后再往下看2個(gè)可以set的property和2個(gè)可以重載的方法:

// default is YES. if NO, we immediately call -touchesShouldBegin:withEvent:inContentView:. 
// this has no effect on presses
@property(nonatomic) BOOL delaysContentTouches;
// default is YES. if NO, then once we start tracking, we don't try to drag if the touch moves. 
// this has no effect on presses   
@property(nonatomic) BOOL canCancelContentTouches;

// override points for subclasses to control delivery of touch events to subviews of the scroll view
// called before touches are delivered to a subview of the scroll view. 
// if it returns NO the touches will not be delivered to the subview
// this has no effect on presses
// default returns YES
- (BOOL)touchesShouldBegin:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event inContentView:(UIView *)view;

// 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;

有了以上這些频祝,已經(jīng)基本能夠理解ScrollView對(duì)手勢(shì)操作的處理原理,這里來統(tǒng)一歸納一下脆淹。我們直接上圖最清晰:

scrollView.png

解釋下上圖:

  1. Apple使用了一個(gè)延遲機(jī)制來判斷在一個(gè)ScrollView內(nèi)是否產(chǎn)生有效的滑動(dòng)手勢(shì)
    常空,而這個(gè)延遲機(jī)制由開關(guān)delaysContentTouches來控制,默認(rèn)為YES
  2. 如果延遲打開且檢測(cè)到有效Scroll盖溺,則將會(huì)直接發(fā)送滑動(dòng)操作到ScrollView窟绷,并停止向SubView發(fā)送任何tracking;
  3. 如果延遲未打開或者打開但是沒有檢測(cè)到有效的動(dòng)作咐柜,則會(huì)看touchesShouldBegin:withEvent:inContentView:的返回:NO則立即返回給ScrollView,否則會(huì)將touch事件發(fā)送給SubView攘残。默認(rèn)為YES拙友。
    當(dāng)touch事件已經(jīng)發(fā)送給SubView之后,如果用戶繼續(xù)產(chǎn)生touch事件(做出Scroll動(dòng)作)歼郭,則:
  4. 檢查canCancelContentTouches開關(guān)(默認(rèn)為YES)遗契。如果為N,則將所有后續(xù)事件發(fā)送到SubView病曾,否則:
  5. 檢查touchesShouldCancelInContentView的返回值牍蜂,如果為N漾根,則將所有事件發(fā)送到SubView,否則返回到ScrollView鲫竞。默認(rèn)情況下辐怕,如果SubView不是UIControl的一種,則返回YES从绘。
  • 一個(gè)簡(jiǎn)單的總結(jié):

針對(duì)1寄疏,2: 默認(rèn)情況下,只要用戶迅速做出滑動(dòng)手勢(shì)僵井,都將觸發(fā)ScrollView滑動(dòng)陕截;
針對(duì)3:默認(rèn)情況下,點(diǎn)擊操作都可以傳入到SubView
針對(duì)4,5:默認(rèn)情況下批什,SubView中的Button, UISlider, UISwitch等等都可以直接響應(yīng)你的Touch农曲,滑動(dòng)事件;但是UIView之類則無法響應(yīng)復(fù)雜事件(Multi-Touch)

  • 想要讓SubView響應(yīng)手勢(shì)操作 驻债?

最簡(jiǎn)單的:

  1. 關(guān)閉delaysContentTouches
  2. 關(guān)閉canCancelContentTouches
    如果想要再多一些自定義乳规,比如有些地方響應(yīng),有些地方不響應(yīng)却汉,則可以:
    touchesShouldBegin:withEvent:inContentView:中設(shè)定響應(yīng)條件驯妄,或者:
    打開canCancelContentTouches,在 touchesShouldCancelInContentView:中設(shè)定響應(yīng)條件合砂。
  • Sample Demo

說這么多還是沒懂青扔?再來一發(fā)Demo,直接看代碼最清晰:
這個(gè)Demo中翩伪,首先自定義一個(gè)ScrollView叫做“MyScollView”微猖,在MyScollView上,有2個(gè)SubView:綠色的greenView和黃色的yellowView缘屹,兩個(gè)View都添加的左右滑動(dòng)的手勢(shì)操作凛剥,但是在touchesShouldBegin:withEvent:inContentView:方法中,當(dāng)檢測(cè)到當(dāng)前view為greenView時(shí)轻姿,返回NO犁珠。
另外,有2個(gè)Switch開關(guān)互亮,分別操作delaysContentTouchescanCancelContentTouches犁享。

Demo - Github地址

我們可以看一下效果:
1) 初始狀態(tài)下,所有開關(guān)打開豹休,UIButton正常工作炊昆,yellowView不能響應(yīng)手勢(shì);
a) 觸動(dòng)yellowView屏幕后馬上滑動(dòng),則UISilder不能正常工作凤巨,ScrollView滑動(dòng)视乐;
b) 觸動(dòng)yellowView屏幕后等待一小會(huì)再滑動(dòng),則UISilder正常工作敢茁;

ScrollView.gif

2) 關(guān)閉canCancelContentTouches
a) 觸動(dòng)yellowView屏幕后馬上滑動(dòng)佑淀,則ScrollView滑動(dòng);
b) 觸動(dòng)yellowView屏幕后等待一小會(huì)再滑動(dòng)卷要,則UISilder渣聚, yellowView響應(yīng)手勢(shì);

ScrollView2.gif

3)關(guān)閉delaysContentTouches僧叉,無論怎樣奕枝,UISilder,yellowView都會(huì)響應(yīng)手勢(shì)瓶堕;

ScrollView3.gif

4)以上任何情況隘道,greenView始終不會(huì)響應(yīng)手勢(shì)

希望你喜歡,歡迎大家討論郎笆。

2016.6.14 完稿于南京

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谭梗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子宛蚓,更是在濱河造成了極大的恐慌激捏,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凄吏,死亡現(xiàn)場(chǎng)離奇詭異远舅,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)痕钢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門图柏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人任连,你說我怎么就攤上這事蚤吹。” “怎么了随抠?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵裁着,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我拱她,道長(zhǎng)背蟆,這世上最難降的妖魔是什么厦幅? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任复旬,我火速辦了婚禮踪宠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘氧猬。我一直安慰自己背犯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布盅抚。 她就那樣靜靜地躺著漠魏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妄均。 梳的紋絲不亂的頭發(fā)上柱锹,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音丰包,去河邊找鬼禁熏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛邑彪,可吹牛的內(nèi)容都是我干的瞧毙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼寄症,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼宙彪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起有巧,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤释漆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后篮迎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體男图,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年柑潦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了享言。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渗鬼,死狀恐怖览露,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情譬胎,我是刑警寧澤差牛,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站堰乔,受9級(jí)特大地震影響偏化,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜镐侯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一侦讨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦韵卤、人聲如沸骗污。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽需忿。三九已至,卻和暖如春蜡歹,著一層夾襖步出監(jiān)牢的瞬間屋厘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工月而, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汗洒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓景鼠,卻偏偏與公主長(zhǎng)得像仲翎,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子铛漓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容