如何使用MVVM ViewModel,其中的属性在willSet/didSet中更新UI元素。

huangapple go评论68阅读模式
英文:

How-to: MVVM ViewModel with properties that update UI elements in willSet/didSet

问题

根据这个问题,这不是正确的做法,但我该如何处理这个问题,以便在更新属性时可以更新UI元素。

在SwiftUI中,可以轻松实现这一目标,使用@Published

var exchangeRate: Double = 1.0 {
    willSet {
        guard let doubleFormattedValue = Double.format(newValue, numberStyle: .decimal, decimalPlaces: 6) else { return }
        DispatchQueue.main.async {
            self.rateLabel.text = String(doubleFormattedValue) + " " + self.fromCurrencyTextField.currency.rawValue
        }
    }
}
英文:

Based on this question, it's not the way to go but how do I go about handling this so I can update UI elements when said property is update.

This would easily be achieved in SwiftUI using @Published

    var exchangeRate: Double = 1.0 {
    willSet {
        guard let doubleFormattedValue = Double.format(newValue, numberStyle: .decimal, decimalPlaces: 6) else { return }
        DispatchQueue.main.async {
            self.rateLabel.text = String(doubleFormattedValue) + " " + self.fromCurrencyTextField.currency.rawValue
        }
    }
}

答案1

得分: 1

Following the suggestion of @Paulw11 and this tutorial, I was able to implement the following:

private var viewModel: CurrencyConverterViewModel = CurrencyConverterViewModel()
private var subscriptions = Set<AnyCancellable>()

private func configureBinding() {

    viewModel.$exchangeRate
        .sink { [weak self] exchangeRate in
            guard let self = self else { return }
            // UI changes go here
        }
        .store(in: &subscriptions)
}
英文:

Following the suggestion of @Paulw11 and this tutorial, I was able to implement the following:

private var viewModel: CurrencyConverterViewModel = CurrencyConverterViewModel()
private var subscriptions = Set<AnyCancellable>()

private func configureBinding() {

    viewModel.$exchangeRate
        .sink { [weak self] exchangeRate in
            guard let self = self else { return }
            // UI changes go here
        }
        .store(in: &subscriptions)
}

答案2

得分: 0

ViewModel不应该包含关于视图的任何信息,你可以使用绑定(binding)与改变处理器(change handler)以及逃逸闭包(escaping)。这个解决方案不使用Combine、RxSwift、async/await等等。

例如:

enum SampleViewModelChange {
    case didValueChanged
    case didError(Error)
    case didEverythingYouWant
}

final class SampleViewModel {
    var sampleChangeHandler: ((SampleViewModelChange) -> Void)?

    var exchangeRate: Double = 1.0 {
        willSet {
            emitSample(.didValueChanged)
        }
        didSet {
            emitSample(.didEverythingYouWant)
        }
    }

    func emitSample(_ change: SampleViewModelChange) {
        sampleChangeHandler?(change)
    }
}

在你的视图中,你可以这样使用:

class SampleVC: UIViewController {
    var viewModel: SampleViewModel

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

    func bindViewModel() {
        viewModel.sampleChangeHandler = { [weak self] change in
            guard let strongSelf = self else { return }
            switch change {
            case .didValueChanged:
                debugPrint(didValueChanged)
                self.rateLabel.text = String(doubleFormattedValue) + " " + self.fromCurrencyTextField.currency.rawValue

            case .didError(let error):
                debugPrint(error)
            case .didEverythingYouWant:
                debugPrint("EverythingYouWant,比如重新加载表格或任何你想要的操作")
            }
        }
    }
}

对我来说,MVVM 不依赖于UIView、SwiftUI或Snappkit,你可以在任何你想要的UI中使用MVVM,这是一种通用的架构。

英文:

ViewModel shouldn't have any inforamtion about the View you can use binding with change handler and escaping, this solution is without combine or rxswift or async await etc.

such as:

enum SampleViewModelChange {
    case didValueChanged
    case didError(Error)
    case didEverythingYouWant
}

final class SampleViewModel {
    var sampleChangeHandler: ((SampleViewModelChange) -> Void)?

   var exchangeRate: Double = 1.0 {
       willSet {
          emitSample(.didValueChanged)
       }
       didSet {
          emitSample(.didEverythingYouWant)
       }
   }

    func emitSample(_ change: SampleViewModelChange) {
        sampleChangeHandler?(change)
    }
}

in your view you can have:

class SampleVC: UIViewController {
    var viewModel: SampleViewModel


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


    func bindViewModel() {
        viewModel.sampleChangeHandler = { [weak self] change in
            guard let strongSelf = self else { return }
            switch change {
            case .didValueChanged:
                debugPrint(didValueChanged)
                self.rateLabel.text = String(doubleFormattedValue) + " " + self.fromCurrencyTextField.currency.rawValue
     
            case .didError(let error):
                debugPrint(error)
            case .didEverythingYouWant:
                debugPrint(EverythingYouWant such as reload table or every thing you want)
            }
        }
    }

}

To me MVVM does not depend on UIView or SwiftUI or Snappkit, you can use MVVM with every UI that you want. this is a general Architecture.

huangapple
  • 本文由 发表于 2023年5月24日 22:07:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76324429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定