在開始閱讀這篇文章之前,建議你先 學(xué)習(xí) RxSwift。
內(nèi)容概覽
- Coordinator模式
- MVVM模式
- RxSwift with Coordinator & MVVM Patterns(內(nèi)含Demo)
Coordinator模式
你有沒有遭遇過這種情況?
你的ViewController之間相互耦合、依賴,導(dǎo)航邏輯分布散亂。
你是否聽過 Massive View Controller問題?
在MVC模式下,ViewController做了過多的工作,比如:初始化代碼、界面跳轉(zhuǎn)邏輯、業(yè)務(wù)邏輯等。
其中就一項(xiàng)任務(wù)本不該由ViewController來完成:屏幕導(dǎo)航的管理和應(yīng)用的執(zhí)行流程。
- 什么是Coordinator模式?
Coordinator模式提供了導(dǎo)航邏輯的封裝,由Soroush Khanlou在2015年的NSSpain會(huì)議上引入iOS社區(qū)。
換句話說,所有的屏幕導(dǎo)航將由Coordinator來管理,而不是讓某個(gè)ViewController通過push或者present方法來顯示另一個(gè)ViewController。
-
采用Coordinator模式有什么好處?
采用Coordinator模式,ViewController之間被隔離開來,而且彼此不可見。
這樣,就可以很方便地復(fù)用ViewController。
正如上面的圖解所示,Coordinator模式的核心思想可以歸納為以下幾點(diǎn):
- 通過Coordinator協(xié)調(diào)一個(gè)或多個(gè)ViewController;
- 每個(gè)Coordinator通過方法(通常叫作start方法)來顯示ViewControllers;
- 每個(gè)ViewController都有一個(gè)delegate引用指向其Coordinator;
- 每個(gè)Coordinator有一個(gè)childCoordinators數(shù)組來持有其子Coordinator;
- 每個(gè)字Coordinator都有一個(gè)delegate引用指向其父Coordinator;
使用示例
Coordinator模式的實(shí)現(xiàn)有比較的方式,所以這里的示例僅供參考。
這里采用兩個(gè)Coordinator來管理3個(gè)ViewController,以此展示一個(gè)Coordinator可以有一個(gè)或多個(gè)ViewController。

首先,創(chuàng)建3個(gè)ViewController,F(xiàn)irstViewController, SecondViewController and ThirdViewController。
每個(gè)ViewController添加一個(gè)Button來導(dǎo)航到下一個(gè)ViewController。

首先,我們創(chuàng)建Coordinator協(xié)議。
協(xié)議包含一個(gè)childCoordinators數(shù)組和一個(gè)只接收NavigationController類型參數(shù)的init方法。
public protocol Coordinator : class {
var childCoordinators: [Coordinator] { get set }
// All coordinators will be initilised with a navigation controller
init(navigationController:UINavigationController)
func start()
}
然后第一個(gè)Coordinator會(huì)管理第一個(gè)ViewController。實(shí)現(xiàn)start方法,把第一個(gè)ViewController添加到NavigationController中。
import UIKit
class FirstCoordinator: Coordinator {
var childCoordinators: [Coordinator] = []
unowned let navigationController:UINavigationController
required init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let firstViewController : FirstViewController = FirstViewController()
firstViewController.delegate = self
self.navigationController.viewControllers = [firstViewController]
}
}
extension FirstCoordinator: FirstViewControllerDelegate {
// Navigate to next page
func navigateToNextPage() {
let secondCoordinator = SecondCoordinator(navigationController: navigationController)
secondCoordinator.delegate = self
childCoordinators.append(secondCoordinator)
secondCoordinator.start()
}
}
extension FirstCoordinator: BackToFirstViewControllerDelegate {
// Back from third page
func navigateBackToFirstPage(newOrderCoordinator: SecondCoordinator) {
navigationController.popToRootViewController(animated: true)
childCoordinators.removeLast()
}
}
FirstCoordinator有兩個(gè)擴(kuò)展,第一個(gè)用于導(dǎo)航到下一個(gè)ViewController,第二個(gè)是導(dǎo)航回FirstCoordinator。
我們需要讓childCoordinators數(shù)組和當(dāng)前的Coordinator棧保持同步。
var window: UIWindow?
// Make the first coordinator with a strong reference
var fisrtCoordinator : FirstCoordinator?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController()
// Initialise the first coordinator with the main navigation controller
fisrtCoordinator = FirstCoordinator(navigationController: window?.rootViewController as! UINavigationController)
// The start method will actually display the main view
fisrtCoordinator?.start()
window?.makeKeyAndVisible()
return true
}
然后,以navigationController作為參數(shù)創(chuàng)建fisrtCoordinator。讓fisrtCoordinator的start來負(fù)責(zé)展示firstViewController。
public protocol FirstViewControllerDelegate: class {
func navigateToNextPage()
}
class FirstViewController: UIViewController {
public weak var delegate: FirstViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
title = "FirstViewController"
}
@IBAction func goToSecondPageAction(_ sender: Any) {
self.delegate?.navigateToNextPage()
}
}
現(xiàn)在,為了導(dǎo)航到下一個(gè)ViewController,我們需要通過delegate告訴Coordinator進(jìn)行導(dǎo)航。
protocol BackToFirstViewControllerDelegate: class {
func navigateBackToFirstPage(newOrderCoordinator: SecondCoordinator)
}
class SecondCoordinator: Coordinator {
var childCoordinators: [Coordinator] = []
unowned let navigationController: UINavigationController
// We use this delegate to keep a reference to the parent coordinator
weak var delegate: BackToFirstViewControllerDelegate?
required init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let secondViewController: SecondViewController = SecondViewController()
secondViewController.delegate = self
self.navigationController.pushViewController(secondViewController, animated: true)
}
}
extension SecondCoordinator : SecondViewControllerDelegate {
// Navigate to third page
func navigateToThirdPage() {
let thirdViewController: ThirdViewController = ThirdViewController()
thirdViewController.delegate = self
self.navigationController.pushViewController(thirdViewController, animated: true)
}
// Navigate to first page
func navigateToFirstPage() {
self.delegate?.navigateBackToFirstPage(newOrderCoordinator: self)
}
}
正如前文所述,一個(gè)Coordinator可以管理一個(gè)或多個(gè)ViewController。所以,這里讓secondCoordinator管理secondViewController 和 thirdViewController。
public protocol SecondViewControllerDelegate: class {
func navigateToFirstPage()
func navigateToThirdPage()
}
class SecondViewController: UIViewController {
public weak var delegate: SecondViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
title = "SecondViewController"
// Use custom back button to pass through coordinator.
let backButton = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(navigateBackToFirstpage))
self.navigationItem.leftBarButtonItem = backButton
}
@objc func navigateBackToFirstpage() {
self.delegate?.navigateToFirstPage()
}
@IBAction func navigateToThirdPageAction(_ sender: Any) {
self.delegate?.navigateToThirdPage()
}
}
請注意:
在切換Coordinator時(shí),NavigationController的返回按鈕的點(diǎn)擊事件方法如果沒有被重寫的話,就會(huì)破壞Coordinator模式的邏輯。我們應(yīng)該重寫NavigationController返回按鈕的點(diǎn)擊事件方法來執(zhí)行Coordinator中的方法,以保證childCoordinators數(shù)組正常更新。
這里是ThirdViewController的實(shí)現(xiàn):
public class ThirdViewController: UIViewController {
public weak var delegate: SecondViewControllerDelegate?
override public func viewDidLoad() {
super.viewDidLoad()
title = "ThirdViewController"
}
@IBAction func navigateToFirstPageAction(_ sender: Any) {
self.delegate?.navigateToFirstPage()
}
}
最后,ThirdViewController調(diào)用代理方法可以回到FirstViewController。
結(jié)論
現(xiàn)在,我們可以通過Coordinator來管理ViewController之間的導(dǎo)航,不需要ViewController之間有任何相互引用。
將導(dǎo)航邏輯移到Coordinator中,有效地削弱了Massive ViewController效應(yīng)。
MVVM模式
- 什么是MVVM模式?

Model:數(shù)據(jù)模型 或 數(shù)據(jù)持久層;
View:用戶交互界面層;
ViewModel:描述模型數(shù)據(jù)的狀態(tài),負(fù)責(zé)轉(zhuǎn)換Model中的數(shù)據(jù);
Binder:綁定View和ViewModel中對應(yīng)的屬性;
- 采用MVVM模式有什么好處?
利于團(tuán)隊(duì)合作:設(shè)計(jì)師可以專注于用戶界面,開發(fā)者可以專注于業(yè)務(wù)邏輯。感興趣的話,請搜索 WPF MVVM;
利于代碼重用:模型、視圖被解耦,更容易復(fù)用;
利于自動(dòng)化測試:模塊耦合度低,模塊化測試容易實(shí)施;
RxSwift with Coordinator & MVVM
Demo采用了Coordinator模式和MVVM模式,部分代碼拷貝于RxSwift官方Demo。
此Demo僅供參考,歡迎讀者提出寶貴的建議,謝謝。
參考文章:
iOS : Coordinator pattern in Swift
Coordinator Tutorial for iOS: Getting Started (Raywenderlich Demo)
3 reasons to use the MVVM pattern
Coordinator 與 RxSwift 共同構(gòu)建的 MVVM 架構(gòu)
Observe Changes on TableView Cell with RxSwift and RxCocoa
如需轉(zhuǎn)載,請注明出處,謝謝 ~
