使用Xib來Layout
最近在複習以前沒注意到的東西, 順便也對AutoLayout做了一點功課, 記錄一下如何使用Xib來快速做一些Layout的動作.
(謎之音:這一段可以直接跳過, 不想看就直接往下吧)
對於大多數的iOS開發者應該都有使用過Interface Builder, 可能也習慣使用IB來做開發; 就個人而言, 其實我更習慣用程式碼來做layout的動作, 即使到了iOS5推出了Storyboard
也沒有改變我的習慣; 最近在追iOS6的內容(相容要到5.0, you know),看到AutoLayout
覺得這真的是一個很棒的功能, 方便、快速, 只是可能太多物件會有點亂(在Xib中), 但是使用Xib來製作就還沒上手的我來說是一個比較容易上手的方式, 順便也嘗試改變一下只用程式來做Layout, 試試看是否會比較快或者是兩個互相搭配有更好的開發方式.
這次會記錄的內容會有
- 使用Xib來定義一個UIView’s Subclass.
- 在ViewController中, 不同的View使用不同的Xib載入內容.
- 使用Xib來定義UITableViewCell, 並直接讓UITableView使用.
內容大多數會差不多, 不過有幾個比較特別的地方會特別點出來
建立UIView subclass with Xib
這邊會有下面幾個步驟
- 建立並宣告一個UIView subclass.
- 為UIView subclass建立Xib並作連結.
- 建立UIView class實體的時候載入Xib內容.
Step. 1-1
首先我們先建立一個Class MyHeader
1 2 3 4 5 |
|
上面的程式碼, 我在.h宣告了兩個property跟一個method, 前面加上IBOutlet
、IBAction
, 這兩個修飾字(是讓Class跟Xib溝通用的), 接下來新增Xib檔案讓MyHeader使用.
Step. 1-2
在Menu的File->New->File, 然後選擇iOS下的User Interface->Empty, 建立檔案並命名為MyHeader
.(參考下圖)
接著開啟MyHeader.xib, 並增加一個UIView到畫面上, 同時將這個View的class設定為MyHeader
, 在MyHeader.h
中我有定義兩個視覺物件分別是一個Label跟一個Button, 所以在剛剛Xib的View我們接著建立Button跟Label, 完成後大該會跟下圖一樣. (這邊跟我們再使用UIViewController, 將 File’s Owner的Class設定成我們的ViewController Class有點不同, 需要注意一下)
接著我們將MyHeader.h
跟MyHeader.xib
需要連接的property跟method連起來
這邊可以發現因為View是MyHeader Class
, 所以在Connections Inspector上會有我們再Class中定義可以跟Xib連結的項目.
到這邊我們已經完成了Xib跟Class的基本設定, 接下來要回到程式的部份, 在建立MyHeader的時候讓MyHeader去載入Xib的內容.
Step. 1-3
在MyHeader.m
中實作init
method, 在init
中我們可以使用兩種方式來載入Xib內容, 不過效果都是相同的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
在這邊, 先介紹第一種方法使用UINib
來載入Xib的內容, 載入完成後在將Xib的資料實體化, 這邊會回傳一個NSArray
包含著Xib所有的內容(top-level objects).
UINib只能使用Class Method來建立實體
在Line4跟5的地方, [nib instantiateWithOwner:nil options:nil]
帶入的Owner跟Options都是nil
, 因為我們再Xib中並沒有對File's Owner
做Class的設定, 所以並不需要帶入(下一個內容會帶入) 不過即使這邊帶入也沒關係並沒有特別影響, 至於options
的部份因為我自己沒有用到, 不過有查到一個相關的內容, 在最後附上給大家參考.
另外, 關於回傳的Array, 因為xib裡面只有一個view, 所以才能用`lastObject`這個method去取得, 如果Xib裡面有多個獨立的view(可以參考第二個內容的圖), 就需要先判斷載入的view的class, 在去做設定.
最後把MyHeader加到RootViewController的View中, 呈現的畫面參考下圖
上面有提到說, 載入Xib的方式有兩種, 一種是使用UINib
, 另一個方式是使用NSBundle
, NSBundle有為載入Xib提供一個Category, 那麼我們修改一下init的內容在嘗試一次.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
修改過後, 呈現的內容將跟使用UINib
一模一樣.
Q&A
Q: 如果再一個ViewController.xib中放入一個MyHeader
View, 這個MyHeader有辦法載入嗎?
A: 我現在嘗試還沒成功, 如果有成功的話在額外補充囉.
在ViewController中, 不同的View使用不同的Xib載入內容.
這邊先稍微解釋一下這個內容跟第一個內容最後的Q&A的差異, 這邊的View不會是一個UIView的Subclass
, 只是讓這個View的載入從另一個Xib去帶入, 跟一般ViewController.xib可能包含不一只一個View(參考下圖), 其中我所選取的是ViewController’s view(灰底), 旁邊有另一個view, 現在這個內容就是把白色的View抽出來到另一個Xib裡面.
接下來我們要建立一個ViewController, 這個ViewController有一個UITableView跟UIView, 其中UIView是要用另個一Xib(不是ViewController所使用的Xib)來載入這個View.
這邊的一些動作跟上面有些類似會省略一些內容, 大致要做的事情有下列
- 建立UIViewController, 讓ViewController有
tableView
跟tableViewHeader
- 為tableViewHeader建立Xib, 並完成連結
Step. 2-1
1 2 3 4 5 6 |
|
接著打開MyViewController.xib, 並在View加入一個TableView, 並且與Class建立連結, 參考下圖
Step. 2-2
接下來我們直接建立ViewController’s header要使用的xib並命名為MyVCHeader
, 這邊比較不同的地方是, 這次的File’s Owner地方, 需要到Identity Inspector
將Custom Class更改為MyViewController
, 這樣一來我們才有辦法在MyVCHeader.xib存取到MyViewController的IBOutlet
參數; 完成後, 在xib建立一個view, 並在view加入幾個subview. 最後再把view跟File’s Owner的header
做連結.(參考下圖)
接下來在MyViewController.m
的viewDidLoad
中, 我們來實作使header從Xib載入, 並將header設定為tableView’s tableHeaderView.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
在執行之後的內容就是上圖; 在這邊你可能注意到, 這次並沒有運用回傳的Array來進行assign的動作, 而是在建立實體的時候, 將owner
傳給帶入method中, 就會自己去做完成最後的connect動作, 這部份其實跟UIViewController在loadView之後的在去載入Xib(最後會到viewDidLoad
)是一樣的動作.
ps. 這邊也可以使用NSBundle來載入內容
使用Xib來定義UITableViewCell, 並直接讓UITableView使用.
這個內容會延續第二個內容製作的項目, 同時運用xib來建立Cell; 接下來會有兩個動作要去完成
- 建立一個UITableViewCell Subclass然後跟Xib做連結(命名為MyCell)
- 用UITableView註冊步驟一建立的UITableViewCell Subclass
Step. 3-1
這個動作跟第一個內容很像, 不過在.m中不需要特別使用UINib(NSBundle)來實作載入xib的動作; 在這邊就不多做說明, 可以參考下面在去實作xib就可以.
1 2 3 4 5 |
|
Step. 3-2
接下來在viewDidLoad
讓TableView註冊MyCell, 這邊註冊的時候會需要帶入一個identifier
, 這個identifier在之後是取得Cell的內容.
1 2 3 |
|
接著再實作UITableViewDataSource取得Cell的mehtod
1 2 3 4 5 6 7 8 9 |
|
上面的程式碼你會注意到, 你不需要在去檢查cell是不是存在的動作, tableview會自己去做reuse跟create新的instance的動作, 這樣就可以直接使用MyCell. 執行的結果參考下圖.
最後
關於用code藍Layout比較快還是用Xib會比較快速, 個人覺得可以是情況而定, 比較複雜的ViewController如果有較多的View需要去做切換, 可以使用xib+code的混合方式, 減少過多的程式碼, 也避免一個xib有太多的view而不好管理, 不過檔案可能會比較多, 就依照個人取捨來選擇作法吧. 如果有任何錯誤, 在麻煩大家幫忙解答一下, 謝謝.
References & Others
最後一個Link有提到關於載入Xib要帶入options
的部份, 如果有興趣可以特別閱讀一下
Resource Programming Guide
NSBundle UIKit Additions Reference
UINib Class Reference
How to use a xib and a UIView subclass together?
How to use a common target object to handle actions/outlets of multiple views?