iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
Mobile Development

在 iOS 開發路上的大小事系列 第 6

【在 iOS 開發路上的大小事-Day06】透過 Delegate 來傳值

前情提要

一般我們在做傳值動作的時候,會有好幾種方式可以做,像是用 Segue、Closure、Delegate、Global Variable、Notification 等方式,每種都有不同的應用場景,所以就依情況來使用~

前面已經有介紹過 GlobalVariable、Segue、Closure 傳值了

今天要介紹的是 Delegate 傳值

我們要做的一樣是將第一個畫面上的 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

參考資料

  1. https://reurl.cc/qg4o3g
  2. https://gist.github.com/matsuda/3b06eb3d3081b059035fc80d4b677c78

上一篇
【在 iOS 開發路上的大小事-Day05】透過閉包 (Closure) 來傳值
下一篇
【在 iOS 開發路上的大小事-Day07】除了用 WKWebView 以外,還可以如何在 App 中顯示 PDF 檔案呢?
系列文
在 iOS 開發路上的大小事30

尚未有邦友留言

立即登入留言