32.%d\n Xcode — Coordinator Pattern(二)

春麗 S.T.E.M.
10 min readDec 7, 2021

--

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/

最後附上

--

--

春麗 S.T.E.M.
春麗 S.T.E.M.

Written by 春麗 S.T.E.M.

Do not go gentle into that good night, Old age should burn and rave at close of day; Rage, rage, against the dying of the light.

No responses yet