iOS之UIScrollview新增約束圖文詳解
原文連結:http://www.jianshu.com/p/e4a12061776d
著作權歸作者所有,轉載請聯絡作者獲得授權,並標註“簡書作者”。
前言
在iOS開發中,autolayout是一個很強大的東西,用好了,能讓你事半功倍,用不好,各種蛋疼。不過autolayout是很容易學的,筆者當時僅僅看了一個多小時的資料就輕鬆上手,相信比筆者厲害的大有人在。
不過無論多簡單的東西,用起來總有不如意的地方,筆者曾經在UIScrollView的約束上折騰了很久,各種報錯,各種查資料,終於在筆者不斷的嘗試下,找到了解決辦法。我相信在工作中肯定有很多跟我一樣,被它折騰的死去活來的同僚,在這裡,筆者將自己的理解分享給大家。
我們先將約束分為以下幾類(僅指子控制元件與UIScrollview之間的約束)
- 1.間距類:既子控制元件到父控制元件上下左右的間距
- 2.寬高類:既子控制元件與父控制元件的寬高比
- 3.居中類:既子控制元件在父控制元件中水平或者垂直居中
首先我們來看一個例子,在普通View上給子控制元件新增約束
木有報錯,很easy的事情
接下來再看看在UIScrollView上給子控制元件新增約束的例子
納尼?同樣的約束,竟然報錯了,以筆者最初的理解,明明都告訴它到上下左右的間距了,為什麼還會報錯,難道是Xcode的bug?
注意
在autolayout中,所有的間距類約束,並非相對於父控制元件本身的,而是相對於父控制元件的內容檢視的(比如UIScrollview的contentSize),由於普通view的內容檢視與本身大小一樣,所以可以看成相對於它自身
scrollView在載入時,會自動根據內部子控制元件來計算contentSize的值(僅指通過xib\sb載入的控制元件,程式碼建立的不考慮在內)。
看完上面兩句話,相信大家應該知道為什麼會報錯了吧,子控制元件的frame依賴了scrollview的contentSize,而contentSize的值又要根據子控制元件的frame來計算,那到底該怎樣?所以Xcode懵了......
在開發中我們可能會遇到這樣的情況,在viewDidLoad中設定了scrollview的contentSize,但是當程式執行時,並不是自己想要的效果,就是因為scrollview根據內部子控制元件重新計算了contentSize的值,因此我們可以在viewDidAppear中來進行設定
說了這麼多,那我們怎麼來解決這個問題呢?廢話不多說,直接開擼
UIScrollview新增約束的正確方式
假設我們要實現如下效果
首先新增UIScrollview到控制器view上,新增約束(這個不用筆者教吧,筆者這裡是上下左右間距為0,所以執行後UIScrollview的大小與螢幕大小一樣)
這裡只講豎直滾動,水平不滾動的情況,其他情況類似,筆者相信各位的智商
傻瓜級
給UIScrollView新增一個唯一的子控制元件UIView(約束如下圖所示),把這個view看成是scrollview的內容檢視(暫且稱它“唯一view”),以後所有的子控制元件都新增到這個view裡面
到父控制元件上下左右的間距固定(0是隨便寫的,自己根據需求決定),固定view的高度(800隨便寫的),加完約束後情況
報錯,正常,報錯是因為水平方向沒有確定,垂直方向是沒有問題的。筆者先來解釋下上面約束的含義,唯一view到scrollview內容檢視的上下左右間距都是0,並且固定高度為800,那麼scrollview會自動計算出contentSize.height = 0(上間距) +800 + 0(下間距) = 800,那麼水平方向呢?contentSize.width = 0(左間距) + 寬度? + 0(右間距) = ?,條件不足,無法計算
特別注意
網上有資料顯示,間距類約束其實是在設定scrollView的內邊距,經過筆者的親自驗證,這種說法是不對的,筆者將間距都設定為10,然後執行程式列印了scrollView.contentInset,結果顯示都是0,且contentSize寬高都多了20,所以間距是算在contentSize裡的,並非contentInset
接下來我們再來新增一個約束
讓唯一view與scrollview等寬,再看結果
沒有報錯,噢耶,以後想新增什麼子控制元件就往唯一view裡新增吧,相對於唯一view新增約束就so easy了。
也許大家會有個疑問,唯一view與scrollview等寬,那到底是多寬,不還是依賴於contentSize麼?
注意
寬高類約束是相對於scrollview本身的,並非相對於它的內容檢視的,所以scrollview有多寬,唯一view就有多寬,與contentSize無關
至此,scrollview的contentSize就可以確定其大小了
contentSize.height = 0(上間距) +800 + 0(下間距) = 800
contentSize.width = 0(左間距) + 唯一view寬 + 0(右間距) = 唯一view寬(scrollview\控制器view\螢幕寬度)
看這裡!!!!
記得把800改了,改成最後一個按鈕的最大Y值(Y + height)+ 下面的間距,如圖
如果在執行時才能確定最後按鈕的最大Y值,可以通過程式碼修改唯一view的高度以及scrollview的contentSize
不改就會這樣
- 1.實際內容小於唯一view高度
多出滾動區域 - 2.實際內容大於唯一view高度
拖不上去
正常級
與傻瓜級類似,只是scrollview的唯一view不再固定高度,而是根據它內部的子控制元件的約束自動調整,如圖
我們看最後一個按鈕的約束,上左右間距+固定高度已經可以確定其位置和大小了,為什麼還要加個下間距?就是為了確定父控制元件唯一view的高度,這裡唯一view的高度 = 按鈕最大Y值 + 下間距
大神級
不要那所謂的唯一view,直接在scrollview裡面新增子控制元件
先新增第一個子控制元件(稱它為“子1”)及約束
子1距離scrollview的內容檢視,上間距為10,左右間距為0
子1與scrollview等寬,它的高度是根據它內部子控制元件約束確定的,這裡就不貼圖了,會autolayout的應該都會,不理解的就當做是固定高度,此時它的位置跟尺寸已經確定,同時scrollview.contentSize.width = 0(左間距) + 子1寬度 + 0(右間距) = 子1寬度(就是scrollview\控制器view\螢幕寬度)。
報錯是因為沒有設定子1到scrollview內容檢視的下間距,暫時不管它(如果看著不爽,可以先新增一個下間距約束,最後記得刪除),繼續往scrollview中新增下面的子檢視,新增完後的效果圖
下面控制元件的約束都一樣(不貼圖了)
- 與上面檢視的垂直間距為10
- 到scrollview的內容檢視左右間距為0
- 固定高度
約束還是報錯,報錯原因與新增子1一樣
接下來新增最後一個控制元件
搞定,木有錯誤了,我們來看看約束
- 1.上間距為10,前面控制元件的位置與尺寸已經確定
按鈕Y值 = 上一個控制元件的最大Y值 + 10
- 2.左右間距為10,前面已經確定了scrollview內容檢視的寬度
按鈕寬度 = 內容檢視寬度 - 2 * 10 按鈕X值 = 10
- 3.固定高度40
上面的約束已經確定了按鈕的frame,那麼最後一個下間距就是為了確定scrollview.contentSize.height按鈕高度 = 40
scrollview.contentSize.height = 按鈕的Y值 + 按鈕的高度 + 下間距
到目前為止,所有子控制元件位置大小確定,scrollview的contentSize確定,最後來看看執行效果。