純Swift專案-Xib | StoryBoard 多人協作技巧
不同於國外, StoryBoard
從面世到如今飽受國內開發者的質疑,質疑的理由很多,什麼不利於多人協作啊,隱藏了UI細節啊,出問題不容易測試,降低執行效率啊等等。此文就是針對這些問題的舉例和剖析。
StoryBoard
和 Xib
有什麼區別?
StoryBoard
和 Xib
都是用來分離UI樣式程式碼,改善檢視程式碼重用率,增加所見即所得,降低檢視測試繁複度的檢視系列化工具,
- 其中
Xib
以檢視View
為主, -
StoryBoard
以控制器Controller
及其之間的關係,以及和檢視View
的關係為主。
實際使用例子參見 《純Swift專案-Xib | StoryBoard 裝置適配技巧》 或其他 StoryBoard
文章
StoryBoard
和 Xib
不利於多人協作, git
合併程式碼容易衝突,且難以處理?
這個是詆譭 StoryBoard
最多的理由,也是 看上去
最充分的理由。最顯著的就是下圖這種失敗的例子。

在一個 Storyboard
中,大量的 Controller
控制器和 Segue
連線彰顯著錯綜複雜的UI關係,使人望而生畏或者難以維護。
但這並不應該是 Storyboard
的鍋,僅僅是使用者對工具的濫用!
沒錯,就是 濫用
,無論是 Storyboard
也好,純程式碼也罷,它們的本質都是工具,工具本身沒有正義或邪惡,影響工具的是使用者。哪怕是用純程式碼開發,如果沒有命名規範,肆意的巢狀 if
,不遵守MVC或者MVVM等開發模式,不區分開發環境與生產環境,這樣寫出來的程式碼又何談可維護性,和多人協作呢?
那麼反過來說,如何使用 Storyboard
才不算濫用?
避免濫用,最好的方法就是定製規範,就好像程式碼中的諸多規範一樣。每個團隊可能有自己不同的喜好,我在此拋磚引玉,列出我們團隊使用 Storyboard
的規範,供大家參考。
每個模組獨立 Storyboard | 每個 Storyboard 只應該有一個主VC和同頁的子VC,主VC不應存在2個以上 |
---|---|
![]() |
![]() |
- 一個專案中, Storyboard 不該是孤立存在的,應該像
MVP
模式那樣,每個頁面都有獨立的 Storyboard ,每個 Storyboard 只應該有一個主 VC 和同頁的子 VC ,主 VC 不應存在2個以上。(絕大多數情況下,一個 Storyboard 上只應該有一個 VC ) - 頁面間的
Segue
連線應該使用Stroyboard Reference Scene
,UITabBarController
的子頁因為複雜度應該當成主 VC 處置 - 檢視的初始樣式應儘量在 Storyboard 上屬性面板中設定,非極特殊情況,佈局也應在 Storyboard 上使用各種約束配合完成。這樣有利於檢視樣式和檢視程式碼分離,有利於檢視程式碼重用性和相容性提高。
- 對於邏輯複雜的 VC ,應新增Object物件,並繫結相應的類來分離邏輯程式碼。
- 對於圓角,背景色,陰影等
CALayer
的樣式,應該使用擴充套件或子類化例項的形式,使用@IBInspectable
屬性關鍵字,在 Storyboard 屬性面板中設定初始樣式。 - 對於自定義檢視,應使用
@IBDesignable
關鍵字保障在在 Storyboard 上所見即所得!
使用以上原則,只要任務分工合理,基本上不存在多人同時修改同一個 Storyboard
的情況,就算配合失誤偶然發生,精簡的 Storyboard 其程式碼量也不大,藉助檔案比較工具很容易就能處理git衝突。
說到底,臃腫的 Storyboard
和臃腫的 ViewController
一樣,都是難以維護且容易 git 衝突的。唯一的解決方案就是有節制的使用工具。
StoryBoard
和 Xib
隱藏了UI細節,且容易導致 ViewController
臃腫?
與其說 StoryBoard
和 Xib
隱藏了UI細節,倒不如說蘋果是希望通過他們來引導開發者正確的使用 檢視 和 控制器 ,他們建立檢視例項的時候都是通過
required init?(coder aDecoder: NSCoder) { } 複製程式碼
構造方法建立檢視例項。所有初始樣式都是在屬性面板中設定的值,通過
func setValue(_ value: Any?, forUndefinedKey key: String) { ...... } 複製程式碼
來賦值給檢視對應的屬性。
至於說導致 ViewController
臃腫,更是荒謬, StoryBoard
提供了多種方案來分離程式碼,只不過很多人不知道而已。
拿美團的主頁UI舉例

這樣的首頁較為複雜,正常佈局的話需要多個 CollectionView
和一個 UITableView

如果這些檢視的 Delegate
都由 ViewController
來實現,自然顯得臃腫且混亂。
一般手寫派會分出3個 ChildViewController
來解決臃腫問題,難道 Storyboard
就做不到麼?

答案是否定的,很早的版本,蘋果就給出了上圖中的解決方案。一個佔位的容器檢視指向子控制器的 Embed Segue

按住 Control
鍵連線到想要包含的子控制器,佔位檢視的例項==子控制器的 view
(子控制器根檢視)

選擇 Embed
連線方式後,子控制器 的尺寸變化成跟佔位檢視一樣的尺寸

這樣我們可以將功能圖示的 CollectionView
的程式碼放到這第一個子控制器上, CollectionViewDelegate
、 CollectionViewDataSource
等程式碼也由子控制器實現
同理,優惠專區可以再新增一個 Container View
,指向第二個子控制器。
通過 Container View
建立的 ChildViewController
如何與主 ViewController
傳參或互相呼叫?
ChildViewController
可以通過 self.parent(Swift)|| self.parentViewController(OC)來拿到主 ViewController
的例項。 主 ViewController
可以通過 self.chilren(Swift) || self.childViewControllers(OC)來拿到 ChildViewController
的例項,它是一個數組,順序等同於佔位檢視再檢視層次中的順序。
值得一提的是,通過此種方式建立的 ChildViewController
,其構造方法晚於主 ViewController
,但生命週期中的 viewDidLoad
則早於主 ViewController
, 因此在 ChildViewController
中的 viewDidLoad
方法中, self.parent 是 nil
,這時不能拿到主 ViewController
例項。如果需要在初始化的時候拿到主 ViewController
的例項,則應該在主 ViewController``viewDidLoad
方法中,呼叫 ChildViewController
的特定方法,把 self 當引數傳過去。
- 除此之外還可以使用Object物件

將它新增到控制器之上。

它的本質是一個繼承自NSObject的子類,我們完全可以把它當成一個小功能模組的控制器。
class FeaturesController: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { @IBOutlet weak var collectionView:UICollectionView! func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { <#code#> } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { <#code#> } } 複製程式碼
在 Storyboard
上選中這個 Object
,繫結上面的類

Object
,在彈出的選單中連線

右鍵 CollectionView
設定 Delegate 和 DataSource 等的連線

在主 ViewController
中如需呼叫這個模組的方法或者傳參
class HomeController: UIViewController { @IBOutlet weak var featuresController:FeaturesController! override func viewDidLoad() { super.viewDidLoad() featuresController.datas = [....] featuresController.collectionView.reloadData() } } 複製程式碼

完成連線,同理,如果一個頁面需要多個子模組,可以在 Storyboard
上拖入多個 Object
,並繫結不同的模組控制類,相對於佔位的 Container View
和 ChildViewController
方法, Object
方法在傳參或互相呼叫方面,更加簡便。缺點是沒有 ChildViewController
的生命週期方法,如需使用 viewWillAppear
等,需要在主 ViewController
的 viewWillAppear
中,呼叫 Object
的自定義方法。
通過上面的2種方法不難看出,並非是 Storyboard
造成 ViewController
程式碼臃腫,而是因為設計不當導致,就算你不用 Storyboard
,把所有功能都寫在一個 ViewController
裡一樣臃腫。這都是使用者決定的,並非 Storyboard
的責任!
StoryBoard
和 Xib
出了問題不容易測試?
這個問題其實問的很模糊,我也是諮詢了很多人才知道,他們所謂的問題不容易測試,是指如下兩種情況:
- 修改或刪除 @IBOutlet 的變數名時,對應的
Storyboard
上未做處理,導致執行時崩潰,崩潰內容看不懂!
StoryBoard
和 Xib
降低執行效率?
(未完待續。。。。)