iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
Mobile Development

SwiftUI 男孩系列 第 27

Day 27: MVVM傳值,不是傳情

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20231011/20130138HUR1bQsZRB.jpg
Photo by Michael on iPhone

跋山涉水終於到達Hooker Lake (胡克湖)🏔️

秋天時期,冰川融化差不多了🧊


整理在 UIKit MVVM 架構下,頁面與頁面之間怎麼傳值,
UIKit 有這些實作招式

  • protocol and delegate
class CustomViewController: UIViewController, CustomViewModelDelegate {

    @IBOutlet weak var titleLabel: UILabel!
    
    var viewModel: CustomViewModel
    
    required init?(coder: NSCoder) {
        self.viewModel = CustomViewModel()
        super.init(coder: coder)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.viewModel.delegate = self
    }
    
    func updateTitle(title: String) {
        self.titleLabel.text = title
    }
}
protocol CustomViewModelDelegate: AnyObject {
    func updateTitle(title: String)
}

class CustomViewModel {
    weak var delegate: CustomViewModelDelegate?
}

// MARK: - Interface Methods
extension CustomViewModel {
    func updateTitleAction(title: String) {
        delegate?.updateTitle(title: title)
    }
}
  • Notification Center
    我就是想練 SwiftUI 寫法
extension Notification.Name {
    enum settings {
        static let domainExpansion = NSNotification.Name("domainExpansion")
        static let 黑閃 = NSNotification.Name("黑閃")
    }
}
struct SettingsView: View {

    @State private var domainExpansion: String = "No Domain Expansion Yet"

    var body: some View {
        VStack {
            Text(domainExpansion)
                .onReceive(NotificationCenter.default.publisher(for: .settings.domainExpansion)) { (output) in
                     if let newDomainExpansion = output.object as? String {
                         self.domainExpansion = newDomainExpansion
                     }
                }
        }
    }
}

Other 頁面

Button(action: {
    NotificationCenter.default.post(name: .settings.domainExpansion, object: "簡易領域")
}) {
    Text("🫸🏻🫷🏻")
}

Tips :

在 SwiftUI 中,你沒有像 UIKit 中的 viewDidLoadviewWillAppeardeinit 這樣的生命週期方法。所以,你不能像在 UIKit 中那樣在 deinit 中調用 NotificationCenter.default.removeObserver(self)

不過,當使用 NotificationCenterpublisher(for:name:object:) 方法與 SwiftUI 的 onReceive 結合時,你實際上不需要手動移除觀察者。當 View 被釋放或消失時,系統會自動處理這些訂閱和取消訂閱。


  • closure + property observers
class CustomViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        viewModel.isViewVisibleChanged = { [weak self] in
            self?.refreshActionButton()
        }
    }

    func refreshActionButton() {
        
    }
}
class CustomViewModel {
    
    var isViewVisibleChanged: (() -> Void)?

    var isViewVisible: Bool = false {
        didSet {
            isViewVisibleChanged?()
        }
    }
}

// MARK: - Private Methods
extension CustomViewModel {
    func change() {
        DispatchQueue.main.async {
            self.isViewVisible = true
        }
    }
}

  • KVO

Objective-C 的產物,所以不特別實作。

Key-Value Observing(KVO)是一種 Objective-C 編程模式,允許一個物件觀察另一物件的屬性的變化。它主要基於 Objective-C 的動態性質。在 Swift 中使用 KVO 有一些要求和考慮事項,但它仍然可以與 NSObject 的子類一起使用。

使用 KVO 的基本步驟是:

  1. 被觀察的物件必須繼承自 NSObject
  2. 被觀察的屬性必須用 @objc dynamic 修飾,以確保它們是動態的。
  3. 觀察者會使用 addObserver(_:forKeyPath:options:context:) 方法註冊它要觀察的屬性。
  4. 當屬性變化時,observeValue(forKeyPath:of:change:context:) 方法會被呼叫。
  5. 使用 removeObserver(_:forKeyPath:) 來停止觀察。

以下是一個簡單的範例:

import Foundation

class Person: NSObject {
    @objc dynamic var name: String = ""
}

class Observer: NSObject {
    var person: Person
    var observation: NSKeyValueObservation?

    init(person: Person) {
        self.person = person
        super.init()

        observation = person.observe(\\.name, options: [.new], changeHandler: { (object, change) in
            if let newValue = change.newValue {
                print("Name changed to \\(newValue)")
            }
        })
    }
}

let person = Person()
let observer = Observer(person: person)

person.name = "John"
// 輸出: "Name changed to John"

person.name = "Jane"
// 輸出: "Name changed to Jane"

雖然 KVO 在 Objective-C 中很受歡迎,但在 Swift 中,很多人傾向於使用其他模式,例如 Delegate、Closure 或 Combine Framework,因為它們提供了更加 Swift 式的語法和功能。

下次介紹 Swift 新的傳值框架

combine


上一篇
Day 26: UIViewControllerRepresentable
下一篇
Day 28 加密解密
系列文
SwiftUI 男孩30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言