Episode 105 — 練習 Unwind Segue、Closure、Delegate、Notification 傳資料

Shien
彼得潘的 Swift iOS / Flutter App 開發教室
10 min readJul 28, 2022

Unwind Segue

class UnwindEmojiViewController: UIViewController {
var currentEmoji = ""
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func choose(_ sender: UIButton) {
currentEmoji = (sender.titleLabel?.text!)!
}

}

首先在第二頁的 controller 中宣告一個字串作為傳資料的變數。

將按到的按鈕的圖片傳給變數

@IBAction func unwind(_ segue: UIStoryboardSegue) {
let controller = segue.source as! UnwindEmojiViewController
emojiLabel.text = controller.currentEmoji
}

將第二頁的按鈕在第一頁的 controller 連 IBAction,要帶入型態為 UIStoryboardSegue 的參數。

segue 的 source 為資料來源,即第二頁的資料。

將在第二頁宣告的 currentEmoji 字串傳給第一頁的 Label。

將三個按鈕連 segue 至該 controller 的 exit,並選擇在第一頁宣告的 unwind 方法。

Closure

class ClosureEmojiViewController: UIViewController {
typealias SendEmoji = (String)->Void
var sendEmoji: SendEmoji!
var currentEmoji = ""

override func viewDidLoad() {
super.viewDidLoad()
}
}

在第二頁宣告一個功能型態的變數來處理傳資料的公能。

currentEmoji 會當作參數傳入該傳資料功能裡。

@IBSegueAction func showClosureView(_ coder: NSCoder) -> ClosureEmojiViewController? {
let controller = ClosureEmojiViewController(coder: coder)
controller?.sendEmoji = { emoji in
self.emojiLabel.text = emoji
}

return controller
}

從兩頁之間的 segue 連出一個 IBSegueAction,從裡面呼叫出第二頁的傳資料變數。將 { emoji in ……. } 傳入二頁宣告的功能變數裡,該功能要把參數顯示在 label 上。

class ClosureEmojiViewController: UIViewController {
typealias SendEmoji = (String)->Void
var sendEmoji: SendEmoji!
var currentEmoji = ""
override func viewDidLoad() {
super.viewDidLoad()
}

@IBAction func choose(_ sender: UIButton) {
sendEmoji((sender.titleLabel?.text)!)
navigationController?.popToRootViewController(animated: true)
}

}

回到第二頁宣告觸發按鈕的功能,觸發按鈕將會呼叫剛剛宣告的型態為功能的變數,傳入按鈕上的圖案。

Delegate

protocol DelegateEmojiViewControllerDelegate {
func delegateEmojiViewController(_ controller: DelegateEmojiViewController, withText emoji: String)
}
class DelegateEmojiViewController: UIViewController {
var delegate: DelegateEmojiViewControllerDelegate!

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}

宣告一個 Protocol 座位兩個 controller 溝通的橋樑,在裡面宣告一個含有需要被傳到另一個 controller 的參數之功能。

在第二頁的 controller 中宣告服從剛剛的 Protocol 之 delegate。符合這個 delegate 的物件代表要幫目前這個 controller 做事。

@IBSegueAction func showDelegateView(_ coder: NSCoder) -> DelegateEmojiViewController? {
let controller = DelegateEmojiViewController(coder: coder)
controller?.delegate = self
return controller
}

從兩頁之間的 segue 連出一個 IBSegueAction。將第一頁的 controller 指派為第二頁的 delegate,代表幫第二頁做事的代理人。

extension ViewController: DelegateEmojiViewControllerDelegate {
func delegateEmojiViewController(_ controller: DelegateEmojiViewController, withText emoji: String) {
emojiLabel.text = emoji
}

}

讓第一頁的 controller 遵從代理第二頁 controller 的 Protocol。呼叫 Protocol 的功能並把傳過來的資料顯示在 Label 上。

class DelegateEmojiViewController: UIViewController {
var delegate: DelegateEmojiViewControllerDelegate!

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}

@IBAction func choose(_ sender: UIButton) {
delegate.delegateEmojiViewController(self, withText: (sender.titleLabel?.text)!)
navigationController?.popToRootViewController(animated: true)
}

}

回到第二頁的 controller 安排 delegate 工作的時機,該時機即是按下三個之中的任一按鈕。

Notification

class NotificationName {
static let notificationName = Notification.Name("emojiNotification")
}

建立一個通知名稱的物件,以防到時候在使用 Notification 時誤打名稱,將使用 static 方法來宣告名稱變數。

class NotificationEmojiViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func choose(_ sender: UIButton) {
NotificationCenter.default.post(name: NotificationName.notificationName , object: nil, userInfo: ["emoji":(sender.titleLabel?.text)!])
navigationController?.popToRootViewController(animated: true)

}
}

在第二頁 controller 中呼叫觸發按鈕的功能,在功能中使用 NotificationCenter 的 default,並呼叫其中 post(name:, object:, userInfo) 來做發出通知的方法。

name 帶入通知名稱

object 為發出通知的物件

userInfo 為通知裡的資料

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self, selector: #selector(showEmoji(noti:)), name: NotificationName.notificationName, object: nil)
}

在第一頁的 controller 接收通知,呼叫 addObserver(_:, selector: ,name: ,object:) 的方法。

_ 為接收通知的物件

selector 為接收通知後要呼叫的功能

name 為通知的名稱

object 為通知的發源物件

@objc func showEmoji(noti: Notification) {
if let userInfo = noti.userInfo {
if let emoji = userInfo["emoji"] as? String {
emojiLabel.text = emoji
print("has emoji")
}
}
}

宣告含有型態為 Notification 的參數功能,要從該參數取的傳來的資料,即為 userInfo 。使用在 post 方法用的 key name(“emoji”),在這裡也用相同的 key name 來取得資料的值。轉換型態來顯示在 Label 上。

--

--