32.%d\n Xcode — Coordinator Pattern(二)
ViewController、TableView、Flow Control
如果我們storyboard裡,有這樣一個轉場的流程,分別是VC裡放了一個button;VC裡放了一個tableView;VC裡放了一個tableView。
至於第一個VC為什麼沒有進入點,可以參考
而這個流程在模擬器上跑起來是這樣的,
起初有份資料如下:
var models: [Model] = [
Model(name: "Charles", age: 38, middleName: "II", zodiac: "豬"),
Model(name: "Chuck", age: 28, middleName: "VI", zodiac: "狗"),
Model(name: "Jason", age: 39, middleName: "IV", zodiac: "雞"),
Model(name: "Chole", age: 29, middleName: "XII", zodiac: "牛"),
Model(name: "Diane", age: 33, middleName: "XIV", zodiac: "猴"),
Model(name: "Larry", age: 88, middleName: "VII", zodiac: "狗")
]
我將它放到第一個VC的程式碼,由第一張可簡單看出,既然有拉segue線,那麼,轉場一定是用segue囉?其實不是,但還是要來複習一下一般轉場傳值要怎麼做。
首先,在第一個VC裡,第一種轉場傳值的方法,
如果要透過segue轉場大致如上,需要透過prepare傳值,我們去找到segue的identifier,再找到destination,利用第二個VC的models來接初始資料。
而segue線可以由button直接show第二個VC,也可以由第一個VC去show第二個VC,如果直接由button,我們就不用performSegue,畢竟還要再寫一次identifier。
接著,第二種轉場傳值的方法,
我們不想透過segue,因為它會跟storyboard綁死死的,所以自己寫一個轉場傳值的方法去擺脫segue線。
當按下button後,觸發了下面的方法,同樣地在storyboard生成第二個VC,把它show出來,並將初始資料裝進去,我們在這裡就已經可以不用拉線了。
到此為止,我們先看下一個VC轉場傳值的方法,記得,第二個VC裡面有一個tableView,用來顯示我們的資料。
方法一,一樣透過segue、prepare,透過didSelectRowAt去performSegue,不過這邊要明確寫出sender,sender是tableView在某個cell的某一欄。
prepare裡又稍稍麻煩了點,我們除了要確定identifier相同,要去找到destination,要確認sender是某個cell,並且還要找到cell的indexPath,如此才能將我們從第一個VC接到的值,傳到第三個VC去。
再來看轉場傳值方法二,
反而便捷了許多,我們找到下一個VC,將選到欄位的值丟到下一個VC的model裡面,再將下一個VC show出來,這就是為什麼我們想要儘量擺脫segue線,擺脫storyboard裡的雜亂。
到這裡看看轉場傳值方法三,
一行??沒錯,就一行,也將該傳的值傳到第三個頁面了。
但是看到delegate就會想到protocol,我們其實還要這樣做,
我們不希望第二個VC還要寫那麼多行程式,於是寫一個protocol,希望有人代替我們做,至於AnyObject是希望屬性能夠是weak的,也只能夠給class去遵循。
那麼我們只要命一個delegate屬性,這一頁的事情就做完了,回頭看看工具人第一頁,
第一個VC必須要遵循protocol才能代理第二個VC,去寫轉場的方法,一樣生成第三個VC並將它show出來。
但是!但是!但是!,第一頁在轉場至第二頁的時候,要記得將第二頁VC的delegate指名為自己,不然這個轉場方法就不會調用了。
接著,回到Coordinator,記得前置動作
刪除target或plist的Manifest的className Main,至於deployment info中的main interface要不要清空就青菜了。
在SceneDelegate裡,
創造一個Coordinator的protocol,讓MainCoordinator這個class去遵循它,
如此,APP顯示的第一個VC就會被MainCoordinator推出來或設定顯示出來,此時,我們也將資料放到MainCoordinator裡,前兩個VC都分別要寫protocol,與命delegate,以讓這個MainCoordinator去遵循,分別實作兩個頁面轉場與傳值的方法。
這些程式碼都放在MainCoordinator裡,於是我們來看看第一跟第二VC,裡面有多少程式碼:
第一個VC:protocol FirstViewControllerDelegate: AnyObject {
func firstVCdidChange(_ firstVC: FirstViewController)
}class FirstViewController: UIViewController {
var models: [Model]?
weak var delegate: FirstViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}@IBAction func buttonTapped(_ sender: UIButton) {
// MARK: -傳值3.1
delegate?.firstVCdidChange(self)}第二個VC:protocol SecondViewControllerDelegate: AnyObject {
func secondVCdidChange(_ secondVC: SecondViewController, didSelect model: Model)
}class SecondViewController: UIViewController {
@IBOutlet weak var secondTableView: UITableView!
var models: [Model] = []
weak var delegate: SecondViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
}extension SecondViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
models.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(SecondTableViewCell.self)") as? SecondTableViewCell else {
return UITableViewCell()
}
cell.setupCell(model: models[indexPath.row])
return cell
}
}extension SecondViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// MARK: -傳值3.1
delegate?.secondVCdidChange(self, didSelect: models[indexPath.row])
}
}
第一個VC不說,第二個VC扣除本來就要用來顯示的tableViewDataSource,只剩下protocol跟呼叫方法傳值。
如此,VC變得乾淨簡單,不用一堆東西擠在一起了。
以下reference
https://www.appcoda.com.tw/coordinator/
最後附上