iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 30
0
自我挑戰組

iOS 新手開發的大小事系列 第 30

Day 30: 兩個 View Controllers 之間傳值 -2

前情提要

昨天介紹了利用 segue 達到正向傳值的方法,今天要介紹另外兩個逆向傳值的方法,有些其中的原理並沒有理解的很清楚,在講述上如果有錯誤,麻煩大家指正。


逆向傳值

Protocol with delegate

此方法是設定好協定 (protocol) 和委任 (delegate),達到傳值,適用一對一 view 的傳值,先設定好兩個畫面,用此方法傳值,必須搭配不使用 segue 的轉場方式,即直接用語法來完成轉場,此篇在第二個 view 上利用滾輪選擇,來回到第一個 view,使第一個 view 的背景顏色轉變為選擇的顏色,也剛好利用此專案來練習 enumeration 搭配 switch 的使用方式

Storyboard 設定的畫面如下,第一個 view 放置到下一頁的按鈕,第二個 view 放置一個 pick view 和一個回到前一個 view 的按鈕,並且為第二個 view 的 Storyboard Identifier 命名,詳情可參閱第三種轉場方法

在第二頁面設定好 pick view 的設定,這次使用 enumeration 來做管理

enum ColorSet: Int, CaseIterable {
        case Grey = 0, Red, Green, Orange, Blue

        var description: String {
            switch self {
            case .Grey: return "Grey"
            case .Red: return "Red"
            case .Green: return "Green"
            case .Orange: return "Orange"
            case .Blue: return "Blue"
            }
        }
        
        var color: UIColor {
            switch self {
            case .Grey: return UIColor.gray
            case .Red: return UIColor.red
            case .Green: return UIColor.green
            case .Orange: return UIColor.orange
            case .Blue: return UIColor.blue
            }
        }
    }
    
    // MARK: - PickView Setting
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return ColorSet.allCases.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

        return ColorSet.init(rawValue: row)?.description
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        backgroundColor = ColorSet.init(rawValue: row)!.color
    }

接著進行傳值的設定,在第二個 view controller 設定需要實作的 Protocol

protocol BackgroundColorDelegate {
    func colorSelected(_ color: UIColor)
}

並且設定 delegate 的變數

 var delegate: BackgroundColorDelegate?

在按下返回的按鈕後把從 pick view 選定的值放入 protocol 的方法中

@IBAction func back(_ sender: UIButton) {
        
        delegate?.colorSelected(backgroundColor)
        dismiss(animated: true, completion: nil)
    }

在第一個 view controller 利用 extension 採用 protocol 並實作其方法

extension ViewController: BackgroundColorDelegate {
    func colorSelected(_ color: UIColor) {
        view.backgroundColor = color
    }
}

記得在第一個 view 中的下一頁按鈕,設定委任

let secondVC = storyboard?.instantiateViewController(withIdentifier: "secondVC") as! SecondViewController
        secondVC.delegate = self
        // Can't combine with performSegue
        present(secondVC, animated: true, completion: nil)

實際執行結果

為方便參考,在此放上完整專案: github

Notification

此方法利用 Notification 的監聽模式來傳值,適用一對多 view 的傳值,同樣也是先設定好兩個畫面

先在第一個 view 中設定 notification key,其形式為 global 變數,而 key 必須是獨一無二的

let redNotificationKey = "com.jes-yang.red"
let greenNotificationKey = "com.jes-yang.green"

設定要監測的名稱

let red = Notification.Name(rawValue: redNotificationKey)
let green = Notification.Name(rawValue: greenNotificationKey)

加入此程式碼取消監測(經查詢,說法不一,有的說在 iOS9 之後以不需要再加入此行,不過在學習的時候,影片仍是有強調需要加入)

deinit {
    NotificationCenter.default.removeObserver(self)
}

官方手冊中關於是否該加入方法 removeObserver()

利用 addObserver(_: selector: name: object:) 方法來讓使用觀察者和通知選擇器以及可選的通知名稱和發送者,將一個條目添加到通知中心的調度表中

// Red
NotificationCenter.default.addObserver(self, selector: #selector(updateTextLabel(notification:)), name: red, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateBackground(notification:)), name: red, object: nil)
        
// Green
NotificationCenter.default.addObserver(self, selector: #selector(updateTextLabel(notification:)), name: green, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateBackground(notification:)), name: green, object: nil)

觀測方法中的 selector 函數

@objc func updateTextLabel(notification: NSNotification) {
        
    let isRed = notification.name == red
    let text = isRed ? "Red" : "Green"
    textLabel.text = text
}
    
    @objc func updateBackground(notification: NSNotification) {
        
    let isRed = notification.name == red
    let color = isRed ? UIColor.red : UIColor.green
    view.backgroundColor = color
    }

在第二個 view,利用方法 post(name: object:) 創建具有給定名稱和發件人的通知,並將其發佈到通知中心,將此方法放入 view 中的 button's action 中

@IBAction func redButtonTapped(_ sender: UIButton) {
    let name = Notification.Name(rawValue: redNotificationKey)
    NotificationCenter.default.post(name: name, object: nil)
    dismiss(animated: true, completion: nil)
}
    
@IBAction func greenButtonTapped(_ sender: UIButton) {
    let name = Notification.Name(rawValue: greenNotificationKey)
    NotificationCenter.default.post(name: name, object: nil)
    dismiss(animated: true, completion: nil)
    }

實際執行結果

為方便參考,在此放上完整專案: github


結語

終於來到鐵人賽的最後一天,沒想到自己可以堅持下來並完成挑戰,這三十天來,紀錄自己在過去幾個月中學習到複習並整理,也偶爾分享一些新試玩的東西,藉由這些紀錄,在查找的過程,也釐清了一些之前不懂的觀念,學習之路並不會因為鐵人賽的結束而終止,希望未來自己能夠更上一層,吸收更多新知後再分享給大家。


上一篇
Day 29: 兩個 View Controllers 之間傳值 -1
系列文
iOS 新手開發的大小事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Howard
iT邦新手 4 級 ‧ 2019-10-16 22:03:42

賀完賽/images/emoticon/emoticon64.gif

我要留言

立即登入留言