【问题标题】:Swift MVVM Bind with BoxingSwift MVVM 绑定拳击
【发布时间】:2020-03-27 15:26:08
【问题描述】:

我只是想创建一个带有 WeatherViewController 的天气应用程序,显示带有 cellstableView,并且当单元格被 轻按时 导致 WeatherDetailsViewController

我使用 boxing 方式进行 binding,如果我在 model 中都设置了 Dynamic type,我会感到困惑strong> 和 viewModel 在下面的示例中。你会明白我的意思。

这是拳击课

    class Dynamic<T>: Decodable where T: Decodable {

    typealias Listener = (T) -> ()
    var listener: Listener?

    var value: T {
        didSet {
            listener?(value)
        }
    }

    func bind(listener: @escaping Listener) {
        self.listener = listener
        self.listener?(self.value)
    }

    init(_ value: T) {
        self.value = value
    }

    private enum CodingKeys: CodingKey {
        case value
    }
}

这是天气模型结构

struct Weather: Decodable {

    let date: Dynamic<Int>
    let description: Dynamic<String>
    let maxTemperature: Dynamic<Double>

    private enum CodingKeys: String, CodingKey  {
        case date = "time"
        case description = "summary"
        case maxTemperature = "temperatureMax"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        date = try Dynamic(container.decode(Int.self, forKey: .date))
        description = try Dynamic(container.decode(String.self, forKey: .description))
        maxTemperature = try Dynamic(container.decode(Double.self, forKey: .maxTemperature))
    }

}

这是我的 WeatherListViewModel 和 WeatherViewModel

在我的 WeatherViewModel 中,我已将类型指定为 Dynamic,但也在模型中指定为绑定到我的 WeatherDetailsViewController,对吗?

class WeatherListViewModel {

    var weatherViewModels: [WeatherViewModel]
    private var sessionProvider: URLSessionProvider

    init(sessionProvider: URLSessionProvider) {
        self.sessionProvider = sessionProvider
        self.weatherViewModels = [WeatherViewModel]()
    }

    func numberOfRows(inSection section: Int) -> Int {
        return weatherViewModels.count
    }

    func modelAt(_ index: Int) -> WeatherViewModel {
        return weatherViewModels[index]
    }

    func didSelect(at indexPath: Int) -> WeatherViewModel {
        return weatherViewModels[indexPath]
    }

}

这是我初始化 WeatherViewModel 的网络获取的 WeatherListViewModel 扩展

func fetchWeatherLocation(withLatitude latitude: CLLocationDegrees, longitude: CLLocationDegrees, completion: @escaping handler) {
    sessionProvider.request(type: WeatherWrapper.self, service: WeatherService.specificLocation, latitude: latitude, longitude: longitude) { [weak self] result in
        switch result {
        case let .success(weatherWrapper):
            let weathers = weatherWrapper.daily.weathers

            self?.weatherViewModels = weathers.map {
                return WeatherViewModel(weather: $0)
            }
            completion()
        case let .failure(error):
            print("Error: \(error)")
        }
    }
}

这是 WeatherViewModel
结构 WeatherViewModel {

    private(set) var weather: Weather

    var temperature: Dynamic<Double>
    var date: Dynamic<Int>
    var description: Dynamic<String>

    init(weather: Weather) {
        self.weather = weather
        self.temperature = Dynamic(weather.maxTemperature)
        self.date = Dynamic(weather.date)
        self.description = Dynamic(weather.description)
    }

}

这是我的 WeatherDetailsViewController 这里我将绑定分别分配给标签以获取更改

class WeatherDetailsViewController: UIViewController {

    @IBOutlet private var imageView: UIImageView!

    @IBOutlet private var cityLabel: UILabel!
    @IBOutlet private var dateLabel: UILabel!
    @IBOutlet private var descriptionLabel: UILabel!
    @IBOutlet private var temperatureLabel: UILabel!

    var viewModel: WeatherViewModel?

    override func viewDidLoad() {
        super.viewDidLoad()

        setupVMBinding()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationItem.largeTitleDisplayMode = .never
    }

    private func setupVMBinding() {
        if let viewModel = viewModel {
            viewModel.date.bind {
                self.dateLabel.text = $0.toString()
            }

            viewModel.temperature.bind {
                self.temperatureLabel.text = "\($0)"
            }

            viewModel.description.bind {
                self.descriptionLabel.text = $0.description
            }
        }
    }

}

问题是,我是否只是在模型和视图模型中重复编写 Dynamic 类型?有没有更好的方法来做到这一点,或者我在正确的轨道上。对不起,长代码示例。

【问题讨论】:

    标签: swift xcode mvvm binding boxing


    【解决方案1】:

    我认为您在 Weather 模型中重复写入 Dynamic。

    它不需要是动态类型。

    您可以创建一个 GenericDataSource

    class GenericDataSource<T>: NSObject {
        var data: Dynamic<T>?  
    }
    

    在您的视图模型中。这将引用您的天气模型,而无需创建动态类型。

    class WeatherViewModel {
        var dataSource: GenericDataSource<Weather>?
    
        ....
    }
    

    在您的视图控制器中

    class WeatherDetailsViewController {
    
        var viewModel: WeatherViewModel?
    
        override func viewDidLoad() {
           viewModel = ViewModel()
    
           var dataSource = GenericDataSource<Weather>()
           dataSource.data = Dynamic(Weather)
    
           viewModel.dataSource = dataSource
    
           setupVMBinding()
        }
    
        private func setupVMBinding() {
            viewModel?.dataSource?.data?.bind {
                self.dateLabel.text = $0.date
                self.temperatureLabel.text = "\($0.maxTemperature)"
                self.descriptionLabel.text = $0.description
            }
        }
    }
    

    【讨论】:

    • 非常感谢,但我想问一下,我用闭包做的“viewModel.date.bind”和你建议的有什么区别。我实现了您所说的,但是仍然对何时使用哪个感到困惑。你也能澄清一下你对天气模型的意思吗?不反对这样做吗?
    • viewModel.date.bind 是正确的,但你有 var date: Dynamic&lt;Int&gt; { return weather.date }。这意味着您绑定到 weather.date 而不是 viewModel.date 的属性
    • 会是这样吗,你可以检查一下WeatherViewModel,我只是把它改成你建议的,并删除了Model中的Dynamic Type,但没有更新。
    • 它看起来对我来说工作正常,但可能是沟通不畅?我不知道。当我在 WeatherDetailsViewController 中执行 DispatchQueue.main.asyncAfter 以更改例如 2 秒后的 temperatureLabel 时,绑定按预期工作。从 viewmodel -> 到 WeatherDetailsViewController
    • 啊,我明白了。您仍然不需要在 Weather Model 中使用动态类型。您可以创建一个 GenericDataSource 类来存储您的 Model 属性。我更新了答案。我想这就是你想要的
    猜你喜欢
    • 2017-11-20
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 2011-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多