上一篇文傳送門:初探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很類似
期待自己未來能熟練且善用這個工具。