首先要明確的是,scrollview 其實和普通的 view 並沒有多大的差別,只不過給它加上了一些手勢和約定。 我們知道,要讓一個 scrollview 能夠滾動的方法是設置它的 contentSize 的寬或者高或者同時比自己的 frame 大。 想一想為什麼要這樣做? 首先,scrollvie ...
首先要明確的是,scrollview 其實和普通的 view 並沒有多大的差別,只不過給它加上了一些手勢和約定。
我們知道,要讓一個 scrollview 能夠滾動的方法是設置它的 contentSize 的寬或者高或者同時比自己的 frame 大。
想一想為什麼要這樣做?
首先,scrollview 被蘋果開發出來是為了通過滾動來顯示比自己的 size 更多的內容,如果它的 contentSize 比自己都小,就沒有滾動的必要了。
但實際上,scrollview 並沒有滾動,當你在“拖動” scrollview 時,你其實是在改變 scrollview 的 bounds.origin。
那麼什麼是 bounds 呢? bounds 和 frame 又有什麼關係呢?
bounds 是一個 view 自身的坐標系,它的作用是規定它自身的內容從什麼位置開始繪製。 frame 是一個 view 相對於自己父視圖的位置。它們是完全不相關聯的。
你已經知道的是,當你“拖動” scrollview 時, scrollview 的代理會收到scrollViewDidScroll: 的消息,在裡面列印 scrollview 的 contentOffset 的值,這個值會隨著你的“拖動”不停的變化。這時候,嘗試列印一下 scrollview 的 bounds.origin 的值,你會發現,這個值竟然和前面的值一樣。contentOffset 只是為了更好的說明情況給 bounds.origin 換了個名字而已。
contentSize 只是抽象的概念,它規定的其實是 scrollview 的 bounds.origin 能夠變化的範圍。
理解到這裡,像其他的 scrollview 的特性,例如 bounces 等,都可以得到解釋。
scrollview 還增加了一個特性,contentInset,它用來 scrollview 增加額外的滾動範圍,其實就是把 bounds.origin 的變化範圍擴大了。當你給 scrollview 設置 contentInset 的時候,你會發現,它的 contentSize 並沒有改變。
一般情況下,你並不會通過直接設置 contentSize 來達到你“滾動”的目的,因為當你想讓 scrollview 的所有內容都能夠通過“滾動”的方式顯示時,你很難計運算元視圖整體的大小, 因為子視圖在當時有可能還沒決定自己的大小。所以通常來說,你會用 scrollview 的子類,例如 tableview 和 textview。他們會根據自己內容的大小自動的設置 scrollview 的 contentSize,以便包裹所有內容。當然他們有自己不同的使用場景。
同樣,當我們試圖實現類似的功能時,我們該怎麼做呢?
這裡我們會用到 Auto layout。只要 scrollview 的子視圖確定了自己的大小,那麼 scrollview 的 contentSize 就確定了,scrollable area 也就確定了。
這裡的最佳實踐是首先在 scrollview 上添加一個四周的 edge constraints 與 scrollview 間距為0的 content view,接下來,把你想要的子視圖添加上去並且設置好相應的約束就可以了。
當我們實現上面的功能後,如果我們還想通過增加一些額外的滾動空間來實現例如防止鍵盤遮擋視圖、下拉刷新或者上拉載入的功能時,我們就可以通過簡單的設置 scroll view 的 contentInset 來實現,而不必從一開始就去關心 contentSize 的大小。
問題思考:scrollView 的代理有 scrollViewDidScrollToTop: 的方法來通知 scrollView 滑動到了頂部,想一想它的原理是什麼?如何知道 scrollView 滑動到底部了呢?如果還要加上考慮 contentOffset 和 contentInset 呢?