1. 程式人生 > >iOS之UIScrollview新增約束圖文詳解

iOS之UIScrollview新增約束圖文詳解

文/codingZero(簡書作者)
原文連結: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
    按鈕高度 = 40
    上面的約束已經確定了按鈕的frame,那麼最後一個下間距就是為了確定scrollview.contentSize.height
    scrollview.contentSize.height = 按鈕的Y值 + 按鈕的高度 + 下間距

到目前為止,所有子控制元件位置大小確定,scrollview的contentSize確定,最後來看看執行效果。