一般我們在做傳值動作的時候,會有好幾種方式可以做,像是用 Segue、Closure、Delegate、Global Variable、Notification 等方式,每種都有不同的應用場景,所以就依情況來使用~
前面已經有介紹過 GlobalVariable、Segue、Closure 傳值了
我們要做的一樣是將第一個畫面上的 TextField 裡的值傳到第二個畫面的 TextView 上
畫面設計這裡就不示範了,大家就自己設計就可以了
Delegate 傳值也是在實作上很常用到的傳值方法之一
話不多說,就馬上進入實作環節!
首先,我們需要先設計一個協定 (Procotol) 來讓 Controller 遵守,我們需要這個 Protocol 來幫我們獲取 FirstVC 上 TextField 的值,所以我們先定義一個 Protocol 叫做 FetchTextDelegate,然後在 Protocol 內定義一個 Method,叫做 fetchTextFromTextField
我們在 fetchTextFromTextField 這個 Method 裡面宣告了一個變數 text,型別為 String,用來接收在 FirstVC 上 TextField 的值
protocol FetchTextDelegate {
func fetchTextFromTextField(_ text: String)
}
接著在 FirstVC 裡宣告一個變數 delegate,型別為剛剛設計的 Protocol,FetchTextDelegate
var delegate: FetchTextDelegate?
接著在跳頁 Button 的 IBAction 裡,將 delegate 委任給 SecondVC 來執行
再呼叫先前在 Protocol 裡定義好的 fetchTextFromTextField 這個方法 ,讓他來取得 TextField 的值,但是用原生 navigationController.pushViewController 來寫的話,在執行跳頁的時候,會出現 Optional,導致 Crash,後面會來解釋如何解決
@IBAction func pushToSecondVC(_ sender: UIButton) {
let controller = SecondVC(nibName: "SecondVC", bundle: nil)
self.navigationController?.pushViewController(controller, animated: true, completion: {
self.delegate = controller
self.delegate?.fetchTextFromTextField(self.textField.text!)
})
}
接著讓 SeocndVC 來遵守 FetchTextDelegate 這個 Protocol,看是要寫在 class 後面,還是用 extension 寫都可以,這邊我用 extension 來寫
// 讓 SecondVC 遵守 FetchTextDelegate 這個 Protocol
extension SecondVC: FetchTextDelegate {
func fetchTextFromTextField(_ text: String) {
textView.text = text
}
}
先前在定義 fetchTextFromTextField 這個 Method 時,將 text 這個變數傳了進去,這邊則是讓 TextView 的值等於 text 的值,而這裡 text 的值就是從 FirstVC 的 TextField 取得的值
然後再來說為什麼用原生的 navigationController.pushViewController 來跳頁的話,會出現 Optional 錯誤,導致 App Crash
因為在執行跳頁的時候,SecondVC 畫面還尚未載入完成,所以找不到 textView 這個元件,所以才會導致 Crash,所以必須來改寫 navigationController.pushViewController,這邊是直接用網路上找到的寫法來解決 (參考2)
import UIKit
extension UINavigationController {
// push with completion handler
public func pushViewController(_ viewController: UIViewController, animated: Bool, completion: @escaping () -> Void) {
pushViewController(viewController, animated: animated)
guard animated, let coordinator = transitionCoordinator else {
DispatchQueue.main.async { completion() }
return
}
coordinator.animate(alongsideTransition: nil) { _ in completion() }
}
// pop with completion handler
func popViewController(animated: Bool, completion: @escaping () -> Void) {
popViewController(animated: animated)
guard animated, let coordinator = transitionCoordinator else {
DispatchQueue.main.async { completion() }
return
}
coordinator.animate(alongsideTransition: nil) { _ in completion() }
}
}
本篇的範例程式碼:GitHub
參考資料