29.%d\n Xcode — Coordinator Pattern(一)
MVVM & C?
這篇基本上是MVVM的延伸練習,但也不是說一定要MVVM才能用,在MVC架構下,仍然是可以使用它的。
Coordinator Pattern,可能有多種翻譯,例如協調者模式,當你習慣於storyboard拉segue後,我們對VC跳轉到另一個VC已經不是什麼問題,當然啦,關於傳值的部份還是需要一些概念,不過這裡只淺談如何跳轉頁面。
同樣的,當你的VC越來越肥大,可能它有三個按鈕,或者三個cell,分別在點擊後要跳轉至不同頁面時,在storyboard裡,我們就要拉三個segue,若VC沒有embed in navigation controller,我們可能還需要unwindSegue,傳值需要prepare、unwindSegue;segue.destination / segue.source等,如果一個VC要著重傳值、著重如何跳轉到其他VC,VC仍然擺脫不了肥大。
viewController,我們希望它最終最終能夠就像個view,那是再好不過。
在這篇有提到,當你新建一個專案時,系統會自動幫你生成一個Main.storyboard及一個viewController,並且以此作為跳過Launch.storyboard後的第一個畫面,如果取消了initial view controller的箭頭,那眼前便一片黑了。
總之,我們希望有自己的storyboard與自己的viewController的話,要做一些前置動作,像是清空專案的Main interface、去掉plist的Application Scene Manifest等。
順帶一提,如果去掉plist的Application Scene Manifest或是去掉target裡,info的Application Scene Manifest,這時都會造成黑屏,這其中,主要的分別便是,當你去掉Main相關,程式會報錯找不到Main.storyboard或者ViewController;去掉Scene Manifest,模擬器跑起來會是黑屏。
然而,我們其實最終只想刪除Main.storyboard,或者連刪除都不想,可是又希望程式的第一個進入點是我們希望的VC,那最簡單的作法便是在SceneDelegate去調整了,要知道的是現在版本的Xcode,AppDelegate都是將Scene交給SceneDelegate去處理,所以我們只要在SceneDelegate去寫code就好了,至於如何回到AppDelegate去寫而不交給SceneDelegate,可以參考上面那篇,在那篇也有基本的頁面跳轉,只是它是透過navigationController與viewController(與以往無異),而不是Coordinator。
開始了!我們先建立一個稱為Coordinator的protocol
先忽略children這個屬性,看coordinator裡面,我們知道需要一個navigationController,為了跳轉頁面後,有一個內建的返回button;接著還有一個eventOccured,方便我們在各種event發生時,做為跳轉頁面的邏輯;最後還有一個需要實作的start。
在下面,還有一個Coordinating的protocol,這是為了讓每個VC遵循此協定後,都記得建立一個coordinator的屬性。
這時,我們可以開始實作一個叫做MainCoordinator的class了
遵循protocol後,就會跳出你該有的屬性,以及你該實作的方法,而我們可以先看事件發生時會做什麼事,用enum去寫event,其中一個case為buttonTapped,在這個case裡,白話一點說,就是點擊按鈕跳轉到第二個頁面,而這個頁面的coordinator就是我這個MainCoordinator,是由我來協調跳轉邏輯,這時我就可以用navigationController去推出第二個頁面,看起來還是很模糊對不對?沒關係,先繼續往下看。
在start方法裡,跟上面一樣的意思,我希望第一個頁面是我在建立MainCoordinator時,將它做為navigationController接上的一個VC。
接著,回到SceneDelegate看看,我們會這樣寫
每個app啟動後,起碼會需要一個基礎的window,前面說了AppDelegate將Scene交給SceneDelegate,所以這個window還過了一個scene,我們先在scene這個func裡面處理一個window實例再指派給系統的window。
我們看到倒數第五行到倒數第二行,簡單解釋為將scene放到window裡;而window接了一個navigationController;window是可見的;指派實例給系統的window。
coordinator是MainCoordinator的實例,它的navigationController是navC,是我們創出來的UINavigationController的實例,從下圖可以清楚看到它的hierarchy。
最後再由coordinator去啟動,我們回頭看start這個func
func start() {
let vc = ViewController()
vc.coordinator = self
navigationController?.setViewControllers([vc], animated: false)
}
程式一開始跳過Launch.storyboard後,會由MainCoordinator去控制頁面的跳轉,而MainCoordinator的navigationController接上的第一個VC是ViewController。
好了,那看看viewController裡寫了什麼
基本上只有這些東西,我們可以看到UI的配置,但找找看與UI無關的東西有哪些?
button加入的action,是吧?可是其實buttonTapped後,即是事件發生後,我們藉由coordinator去跳轉到第二個頁面,僅短短一行而已,此時VC看起來不那麼肥大了。
重新回顧一下,MainCoordinator做了什麼事?start( )幫助我們把第一個scene設為VC,這個VC embed在navigationController裡;而eventOccured的buttonTapped,叫我們跳轉至第二個頁面。
而再想想Coordinator這個protocol,要我們在建立一個遵循這個protocol的協調者class時,要建立naigationController這個屬性,與實作兩個func。
那麼,Coordinating這個protocol,是要我們記得在VC建立一個coordinator這個屬性,透過讀取各個VC的coordinator屬性,我們就可以在MainCoordinator裡去寫跳轉頁面的邏輯了。
以下reference
最後附上