初探Closure(二):利用Closure傳送參數,讓其他ViewController把獲得的值傳入XIB View

Murphy
6 min readApr 15, 2024

--

上一篇文傳送門:初探Closure(一):利用Closure讓自訂XIB畫面與ViewController互動

上一篇文,我們做到了從XIB呼叫closure讓ViewController去做present
這篇我們就來探討第二部份:由Date Picker選擇日期後,傳回原本ViewController內將XIB View上日期Button修改為Date Picker選擇的日期

不免俗的,再次用圖像釐清一下現在要做的事情

首先,我們一樣來宣告closure,但這次我們要宣告的是有帶參數的closure

import UIKit

class DatePickerViewController: UIViewController {
//UIDatePicker
@IBOutlet weak var datePicker: UIDatePicker!

//帶參數的closure
var returnDate: ( _ : Date?)->() = { _ in }

}

說明一下這個closure的內容:
我宣告一個closure,他必需給一個Date型態的參數(這個參數有可能是nil),而決定closure內要做什麼的物件要記得給變數讓傳入的參數接起來
(也就是 { _ in } 這段)

接下來,我在ViewDidLoad裡面幫Date Picker增加一個target

import UIKit

class DatePickerViewController: UIViewController {
//UIDatePicker
@IBOutlet weak var datePicker: UIDatePicker!

//帶參數的closure
var returnDate: ( _ : Date?)->() = { _ in }

override func viewDidLoad() {
super.viewDidLoad()
//當值被改變時(UIControl.Event.valueChanged),執行@objc dateChanged()
datePicker.addTarget(self, action: #selector(dateChanged), for: UIControl.Event.valueChanged)
}

@objc func dateChanged(){
//呼叫closure,並傳入datePicker選到的日期
returnDate(datePicker.date)
dismiss(animated: true)
}
}

OK! 我把參數傳出去了,那誰來接呢?
當然就是原本擁有XIB View的那個viewController囉!

上一篇文有提到我用的viewController present Date Picker畫面,需注意的是present的同時我也在等待他告訴我我要把日期Button改成什麼值。所以我需要在closure結束前接到我要修改的值。

import UIKit

//ViewController
class DetailViewController: UIViewController {

//XIB View
@IBOutlet weak var editView: InputView!

override func viewDidLoad() {
super.viewDidLoad()
//第一個closure:present Date Picker畫面
editView.presentDatePickerClosure = { [weak self] in
guard let selfVC = self else { return }
let datePickerSB = UIStoryboard(name: "your storyboard name", bundle: nil)

//因為將datePicker的viewController放在不同的storyboard,
//因此注意在抓ViewController時須將Class轉型
let datePickerVC = datePickerSB.instantiateViewController(withIdentifier: "DatePickerViewController") as! DatePickerViewController
selfVC.present(datePickerVC, animated: true)
//------以上為上一篇的內容------


//第二個closure:把closure傳過來的參數指給XIB View的日期Button
datePickerVC.returnDate = { [weak self] assignDate in
guard let selfVC = self else { return }
if let date = assignDate {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd"
selfVC.editView.dateBtn.setTitle(formatter.string(from: date), for: .normal)
}
}
}
}
}

成果如下

我們來整理一下Closure的宣告方式:

//不帶參數的closure
var presentDatePickerClosure: ()->() = {}

//帶參數的closure
var returnDate: ( _ : Date?)->() = { _ in }

//帶多個參數的closure
var mutipleClosure: ( _ : String?, _ : String?) -> () = { _ , _ in }

Q & A

Q: Closure由誰宣告?由誰呼叫?
A: 由決定何時要執行closure的物件宣告及呼叫
(本範例即為XIB View及被present出來的DatePickerViewController)

Q: 由誰決定執行的內容?
A: 由需要實際對畫面或邏輯進行一些處理的物件來寫closure執行的內容,當然一開始就將closure的內容在宣告時寫好也是可以的
(本範例為DetailViewController)

後記:
在學習closure時,有種腦被門夾到的混亂感 ( ? )
但釐清之後,個人覺得closure的概念跟delegate很類似
期待自己未來能熟練且善用這個工具。

--

--