i 3:scrollview

內(nèi)容來自于objc.io

可以說scroll view是iOS 生動(dòng)特性的一個(gè)非常重要的注腳,而在完全掌握它之前,你甚至?xí)幸环N夜不能寐的感覺,接下來,讓我們細(xì)細(xì)剖析scrollview

由于scrollview的特性其實(shí)只是來自于UIView 特性的疊加,所以對(duì)scrollview的理解更多來自對(duì)UIView的理解,如下是兩過程的view渲染的細(xì)節(jié):

Rasterization and Composition

第一個(gè)步驟稱為光柵化,它只是執(zhí)行一些繪制指令并產(chǎn)生一份圖像。比如按鈕只是繪制一個(gè)圓角矩形,并在中間繪制文字,這些內(nèi)容由view持有,等待交由第二個(gè)步驟使用。一旦每個(gè)view均執(zhí)行得到了光柵化圖像,則會(huì)使用稱為 合成 的過程將它們組合成屏幕大小的圖片。view層級(jí)在合成過程中起到了很重要的作用:子view會(huì)覆蓋在父view之上。最頂層的view是window,而其合成的內(nèi)容是用戶最終看到的內(nèi)容。

這個(gè)時(shí)候重點(diǎn)來了,大家都知道view均有frame和bounds屬性,它們有相同的size(除了transform屬性所帶來的影響之外),但orgin通常不一樣,理解這兩個(gè)屬性的原理就可以理解scrollView的原理。

在光柵化的過程中,view并不關(guān)心它的frame(決定view的位置和大?。┖驮趘iew層級(jí)中的位置(決定其合成的順序),而只關(guān)心自己的繪制內(nèi)容,繪制發(fā)生在每個(gè)view的drawRect方法中。

在drawRect調(diào)用之前,會(huì)為view創(chuàng)建一個(gè)空白的image以供繪制。這個(gè)image的坐標(biāo)系統(tǒng)是其bounds,如果在bounds之外繪制,那這份繪制并不會(huì)成為光柵化圖像的一部分,并會(huì)被廢棄。雖然ios底層的繪制過程使得可以將子view在superview的bounds之外渲染出來,但在光柵化的過程中,在bounds之外繪制的內(nèi)容是會(huì)被廢棄的。

Scroll View’s Content Offset

以上這些跟scrollview有什么關(guān)系呢?答案是關(guān)系大發(fā)啦。想象一下滾動(dòng)的時(shí)候發(fā)生的事情:在拖拽過程中,我們改變了view的frame,如果往右拽,會(huì)增加origin.x

之前在合成的時(shí)候計(jì)算子View在父view中的位置時(shí),父view的bounds.origin 通常是{0,0},所以子view.frame.origin即對(duì)應(yīng)父view中對(duì)應(yīng)坐標(biāo)的點(diǎn)。但如果父view的bounds.origin不為0的時(shí)候,則需要將子view.frame.origin+父view.bounds.origin。

所以更改bounds的origin可以調(diào)整子View在父view中顯示的位置,而且實(shí)際上,scrollview的contentOffset屬性即是通過調(diào)整bounds.origin完成滾動(dòng)的。

Content Size

有了關(guān)于contentOffset的理解,接下來關(guān)注下contentSize

contentSize并不會(huì)更改scrollview 的bounds,所以不會(huì)影響Scrollview合成子view。scroll view的默認(rèn)contentSize是{w:0,h:0},由于沒有可滾動(dòng)的區(qū)域,用戶不可以滾動(dòng),但scrollview仍然會(huì)在其bounds中顯示所有子view。

當(dāng)contentSize比Scrollview大的時(shí)候,才允許滾動(dòng)

上圖中visible area的bounds應(yīng)當(dāng)是{80,40,200,300}

當(dāng)contentOffset為{0,0}時(shí),可見窗口的左上角正好是可滾動(dòng)區(qū)域的左上角,這也是contentOffset的最小值,最大 contentOffset 是contentSize與Scrollview.bounds的差值。

Tweaking the Window with Content Insets

contentInset可以改變contentOffset的最大和最小值(顯示上而已,因?yàn)閏ontentOffset的最小值仍是{0,0},最大值亦不變)

contentInset看起來很有用,但為什么不直接更改contentSize呢,以UITableView為例,它已經(jīng)精準(zhǔn)地根據(jù)各Cell的情況算出了contentSize??紤]使用UIRefreshControl的情況:不能將UIRefreshControl放在可滾動(dòng)區(qū)域中,因?yàn)檫@樣會(huì)使得用戶可以滾動(dòng)經(jīng)過UIRefreshControl并停留在UIRefreshControl的上方,并無(wú)法主動(dòng)彈回到第一個(gè)Cell的上邊界。所以需要將UIRefreshControl放置在可滾動(dòng)區(qū)域的上方,這樣使得contentOffset可以彈回第一行,而不是停留在UIRefreshControl上。

等一下,當(dāng)滾動(dòng)得夠遠(yuǎn)以致于觸發(fā)了refresh時(shí),這時(shí)tableview并沒有彈回第一行隱藏refreshControl是因?yàn)槭褂昧薱ontentInset。而當(dāng)刷新結(jié)束時(shí),會(huì)恢復(fù)contentInset,此時(shí)contentOffset保持原值,且不需要對(duì)contentSize做新的計(jì)算,維持原值即可,此時(shí)view恢復(fù)到將refreshControl隱藏。

那么在代碼中什么時(shí)候應(yīng)該用到contentInset呢:一個(gè)極佳的例子是鍵盤出現(xiàn)的時(shí)候。

當(dāng)然還有zooming,今天不會(huì)討論這個(gè),但有一個(gè)有趣的地方可以注意下:從viewForZoomingInScrollView:返回的時(shí)候,檢查transform屬性,可以發(fā)現(xiàn)scrollview巧妙地運(yùn)用了UIView現(xiàn)存的屬性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 可能你很難相信UIScrollView和一個(gè)標(biāo)準(zhǔn)的UIView差異并不大,scroll view 確實(shí)會(huì)多出一些方...
    評(píng)評(píng)分分閱讀 767評(píng)論 2 9
  • 自己簡(jiǎn)單總結(jié): 點(diǎn)就在理解UIView渲染的subview 光柵化圖片時(shí)候組合的過程 公式: Composited...
    大餅炒雞蛋閱讀 370評(píng)論 0 1
  • 原文理解 Scroll Views 可能你很難相信 UIScrollView 和一個(gè)標(biāo)準(zhǔn)的 UIView 差異并不...
    難卻卻閱讀 392評(píng)論 0 0
  • 原文鏈接:Understanding Scroll Views 可能你很難相信,UIScrollView和一個(gè)標(biāo)準(zhǔn)...
    nadou23閱讀 411評(píng)論 0 0
  • 可能你很難相信 UIScrollView 和一個(gè)標(biāo)準(zhǔn)的 UIView 差異并不大,scroll view 確實(shí)會(huì)多...
    燃燒的木頭閱讀 585評(píng)論 2 1

友情鏈接更多精彩內(nèi)容