1. 程式人生 > >UIScrollView新增子控制元件約束的一些小總結

UIScrollView新增子控制元件約束的一些小總結

之前在使用AutoLayout給UIScrollView進行佈局的時候,總會出現點這樣那樣莫名其妙的問題.我也曾跳坑兩次,掙扎許久最後都以放棄storyboard改為程式碼實現而告終.今天終得正解,遂拿出來說說.

先從最基礎的開始,我們試著在storyboard上新增一個UIScrollView,並且在內部新增一個和它一樣大的UIImageView.

首先,拖一個UIScrollView到storyboard,設定約束如下:

頂部距離控制器view距離為50
左側和右側距離控制器view距離分別為0
高度固定250(多吉利的數字)

scrollView的約束

然後拖一個UIImageView到scrollView中,我們想讓它在{0,0}處並和scrollView的尺寸一樣,於是設定上下左右距離父控制元件都為0:

imageView相對scrollView的約束

好像沒什麼問題,可是...


沒看錯,Xcode告訴我們這樣安排約束是錯誤的.
UIScrollView的子控制元件新增約束與普通view不同,僅僅這4個約束不足以滿足它的需求.

那麼,怎樣才是正確的做法呢?

首先:

scrollView自身的約束(scrollView的位置和尺寸)可以像正常的UIView一樣參照其父控制元件新增.

正如上面我們第一步所做的,在給scrollView新增子控制元件之前,那四個約束決定了scrollView的大小和位置,這步是沒有問題的.

問題的關鍵在於如何給scrollView內部的子控制元件新增約束.

scrollView內部子控制元件約束的新增需要遵循兩個原則:

1、scrollView內部子控制元件的尺寸不能以scrollView的尺寸為參照
2、scrollView內部的子控制元件的約束必須完整

首先,子控制元件的尺寸不能以scrollView的尺寸為參照,那麼我們有兩種選擇:

  • 提供一個具體值的約束(比如200)
  • 子控制元件的尺寸可以參照scrollView以外其它的控制元件的尺寸(如控制器的view的尺寸)

其次,約束"完整"的意思是說:子控制元件在水平及豎直方向上的約束要把scrollView"撐滿".

也就是說,在水平方向上,我們需要設定:

  • 子控制元件左側與父控制元件的距離
  • 子控制元件自身的寬度
  • 子控制元件右側距父控制元件的距離.

豎直方向上也一樣,要設定:

  • 子控制元件頂部距父控制元件的距離
  • 子控制元件的高度
  • 子控制元件底部距父控制元件的距離.
    如圖:

scrollView子檢視的約束條件圖1
scrollView子檢視的約束條件圖2

兩張圖片中,所有紅色線條的長度都要確定(黃線表示對齊),才能保證AutoLayout不會報錯.

為什麼scrollView如此隔路(隔路:特殊,與眾不同)呢?

這是因為,scrollView需要根據新增在其內部的子控制元件的寬高及與四周的距離計算出它的contentSize.

舉個栗子:
一個新增在scrollView內部的imageView的寬高為{80, 50}, imageView距離上左下右的距離分別為:100, 200, 300, 400,那麼不需要用程式碼賦值contentSize,我們就可以打印出scrollView的contentSize為{680, 450}.
如圖:


IB新增約束的原理

如果理解起來還是有困難,我們可以把scrollView的contentSize的範圍想象成一塊UIView(上圖中的藍色區域),暫且叫它container(實際是沒有這個東西的).當我們在storyboard或xib中設定子控制元件與scrollView之間約束時,實際上設定的是子控制元件與container之間的約束.

也就是說,子控制元件的約束決定了container的尺寸(contentSize).
這就說明了為什麼我們要在水平和豎直方向用約束"撐滿".如果不撐滿,container不知道它自己應該多大.

也正是因為container的尺寸由子控制元件的約束決定,所以子控制元件的尺寸不能再反過來參照container的尺寸.不然你等於我,我等於你,那到底是多少呢?如果你是Xcode你也會抓狂.(再次強調那個container不是真的,只是為了方便理解)

理論部分解釋完畢,回到一開始的案例上.

imageView有了上下左右四個約束,還缺少寬高,我們再添加個固定寬高的約束(可以大一點,太小了看不到滾動效果),問題就可以解決了.

強調一下, 用程式碼給scrollView新增約束(包括使用Masonry的情況)是一樣的.