AutoLayout 實現固定寬度動態高度的 ScrollView

發表於2015-09-28

作為一個iOS 開發者,很多情況下會需要把一個和螢幕等寬的 contentview 新增到一個 scollrview 內部中。大多數的 app 需要有響應式佈局,所以使用 Autolayout 可謂明智之選。第一次學習 scrollviews 時,你或許會覺得碉堡了。但它們奇怪的規則也會令人有點沮喪。

準備

你對 Xcode 和 interface builder 都足夠熟悉。

在 ScrollView 內的 Content View

這篇文章重點在 autolayout,所以我要用各種方式來告訴你如何處理它。第一個方式是 interface builder,因為這是最直觀的。

設定垂直滾動檢視的 interface builder

建立一個新的單檢視控制器應用程式,開啟 storyboard,新增一個 scrollview 到 storyboard 上的ViewController 上。使 scrollview 填滿整個 viewcontroller 的 view。然後使用 storyborad 右下角的 pin 選單,給 scrollview 新增上、左、右、下的約束。確保你沒有選中“constrain to margin”核取方塊。圖1 顯示了正確設定的 pin 選單。這將新增 scrollview 和它的父檢視之間的約束。

圖1:給scrollview新增 上,左,右下的約束。

現在你想新增一個標準的 view 到 scrollview 上。就如你剛剛給 scrollview 新增上、左、右、下的約束那樣,給這個新 view 也新增這些約束,並確保這些約束的值都是0。

當你完成後,通過選中 view 皮膚中的 view ,檢視在右側的 Size Inspector, 可以檢視新增到 scrollview 和 Container view 的約束。圖2 顯示了內容檢視, 但 scroll view 應該看起來也是一樣的。

圖2:Size inspector 顯示在 scrollview 和 container view 上的約束。

ok,現在容器檢視裡已經有了一個 container view。有人會認為,因為我們給 scrollview 的邊緣新增了上、左、下、右的約束,所以這個 container view 將會一直在 scrollview 的上面,並且擁有和視窗一樣的寬度。但事實並非如此,因為 scrollviews 略有不同,這些約束定義了 scrollview 的 content size ,但因為我們的 view 沒有一個確切的 width 或 height ,所以這個 content size 的 wide 和 tall 都是0。

通常我們都想要一個 scrollview 只能垂直滾動,所以並不想讓 container view 比視窗寬,而高度我們通常是想要動態的,所以它會像其內部的 contents 一樣高,稍後再說動態高度,現在將解決固定寬度的問題。

設定垂直滾動

我們要確保 containner view 的寬度不會超過 window 的大小,為此我們將 container view 和 main view (包含 ScrollView 的檢視)設定成等寬,在 view inspector 中,按著 ctrl 拖拽 main view 到container view 並從彈出的選單中選中 EqualWidths。圖3 顯示了被連線的2個檢視。

圖3 按著 Ctrl 拖拽 main view 向 container view

設定動態 Container view 高

最後一步,沒有那麼多的步驟指導。基本上,為了這項工作,container view 上所有的子檢視必須要有一個高度。一些子檢視可以使用它們固有的高度,通常是由它們的寬來決定的。一般地,你需要為任何沒有包含文字的檢視指定寬度和高度。這些包含文字的檢視至少有一個指定的寬或 margins。

第一個和最後一個檢視應該分別以 container view 的頂部和底部分別固定。例如 在圖4中,注意在圖4中所有的檢視都有左和右的約束,這將決定每個檢視的寬度。labels 不具有高度的約束,因為它將取決於其內容的大小。方形的檢視有確切的高度約束。還要注意所有的檢視頂部和底部之間有一個顯示的垂直約束。綜合這些就能決定 contanier view 的高,還有scrollview 的 content size。

在圖4中顯示如下約束。

  • Top Label
    • 距 container view 頂約束:50pt
    • left: 15pt
    • right: 15pt
    • 底部約束(同為box頂約束): Standard
  • Box label
    • 頂部約束(同為top label底約束): Standard
    • left:15pt
    • right: 15pt
    • 底部約束(同為bottom label頂約束): Standard
    • 高度:86pt;
  • 底部label
    • 居上約束(同為box底約束): Standard
    • left:15pt
    • right:15pt
    • 距 container view 底約束: Standard

圖4 新增子檢視到容器檢視示例

注意:如果要想 label 的 content 自動增長,就要先選中label並在屬性檢查器重設定 label 的行數為”0”。這樣 label 的行數就沒有限制了。

現在給底部的lable設定一段文字並執行這個例子,將會觸發垂直滾動的條件。圖5 顯示了堆疊檢視。從圖5中可以看出,在容器檢視頂部圖4所示的單個檢視,容器檢視堆放在 scrollview 上,然後是 main view,最後是 window。

圖5 模擬堆疊檢視

設定垂直滾動檢視程式設計

ok,這個部分將希望鞏固最後一節概念,建立一個single-view appliction的專案並開啟ViewController.h檔案。

注意:如果你熟練使用 Xcode 你也可以在原來的 storybord 上拖進一個新的 ViewController,建立一個類,設定這個ViewController的類為你新建立的那個類。你可以在右側的Identity Inspector裡設定.然後把這些類目放入一標題欄裡。

在ViewController.h裡,需要建立以下屬性:

  • “contentView”命名的UIView
  • “scrollView”命名的UIScrollView
  • 2個UILabel ”topLabel”和”bottomLabel”
  • “boxView”命名的UIView

當你完成你的程式碼後,你的ViewController.h檔案,應該和程式碼1一樣。

程式碼1 設定屬性

 

在 ViewController.m 裡,第一步需要在 viewDidload 中需要設定scrollView的寬度。如程式碼2中所示的新增 scrollView 和 contentView。

程式碼2:在主檢視中新增滾動檢視和內容檢視

 

再一次我們設定 scrollview 相對於 view 的 margins 為0 ,contentView 相對於 content viewmarigins 為0。最後將 content view 的 width 與 main view 的 width 設為一致。這些都是以程式設計方式新增的約束。將程式碼3中的程式碼新增到 viewDidLoad 方法裡程式碼2的後面。

程式碼3:新增contentView和scrolView的約束

 

在這份程式碼中,我們使用了兩種不同型別的約束。第一種使用的是 Visual format language,這是一種很好的新增多個約束的字串描述。例如:

  • |意思是superview
  • .意思是view之間的空間(單個 – 意味著 standard)例如.-(0)- vs. –
  • (某個值value)意思是寬
  • [某個view]意思是一個view

把它們連線在一起作為第一個約束 @“V:|-(0)-[scrollView]-(0)-|”

這行程式碼的意思是給scrollview新增距 superview 左右都為0的 margin。

更多關於visual format languge 請檢視文件:Visual Format Language

寬度約束是將約束新增到檢視的標準方式,你可以看得更清楚,但是有點繁瑣。

最後,呼叫一個新的方法,把剩餘的檢視新增到content view中,我們把它叫做 addContentSubViews。

程式碼4 展示了這個複雜的 viewDidLoad 方法。

程式碼4 完整的 viewDidLoad 方法

 

addContentSubViews方法十分簡單,僅僅建立了幾個label和一個box view.label的numberoflines = 0,意味著lable的content會滿足多行的需求。label 居中且自動換行。

程式碼5 展示了應該新增下面的viewDidLoad方法完整的方法。

程式碼5: addContentSubViews 實現

 

在程式碼5 中 我們實現了兩個新方法。一個簡單地返回一個大字串。程式碼6展示 bottomLabelText 方法的實現。

程式碼6: bottomLabelText 實現

 

最後一種方法將新增所有的約束來定義內容檢視的內容高度。程式碼7是 addContentSubViewConstraints 的最終實現,它新增了和IB中所展示的完全相同的約束。

這是相當多的。程式碼7展示了完整的viewController.m檔案。

程式碼7:viewcontoller.m完整檢視

 

 

噢, 別忘了 swift,程式碼8 是完整的 swift 版實現。

程式碼8 :完整的swift 控制器

1
GeSHi Error: GeSHi could not find the language swift (using path /home1/jhoffman/nscookbook/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

注意:使用 swift 時我不得不為 content view 定義一個高,這如沒有任何意義一般,因為幾乎是逐字的將程式碼轉換為 swift.目前看來像是一個 swift bug,有任何訊息我會通知你,我猜在 swift 2 時會表現的更好。好了,這期就到這裡了,希望對你有所幫助。

相關文章