我一直在寻找一种在变量更新时自动更新UI的简单方法。常规KVO是如此混乱的代码,所以我一直在研究RxSwift、ReactiveCocoa等,但发现这些很难掌握,还有很多新对象和太多我不需要的东西。
我一直在玩一个变量的didSet
,以及我的对象中的emtpy可选函数。希望展示起来比解释起来容易:
自定义对象(视图模型)
class AwesomeViewModel{
var awesomeText:String { didSet { updateBlock?() } }
var updateBlock:(()->())?
init(awesomeText:String){
self.awesomeText = awesomeText
}
}
和自定义UIView:
class AwesomeView:UIView{
@IBOutlet weak var awesomeLabel: UILabel!
func bindViewModel(viewModel:AwesomeViewModel){
viewModel.updateBlock = { [weak self] in
self?.awesomeLabel.text = viewModel.awesomeText
}
viewModel.updateBlock?()
}
}
假设这个自定义视图AwesomeView
作为出口存在于UIViewController
中。我创建了一个AwesomeViewModel
的实例,然后调用self.awesomeView.bindViewModel(awesomeViewModel)
。
稍后,当我在UIViewController
(或其他任何地方,就这一点而言,它可以传递)中执行类似awesomeViewModel.awesomeText = "Hello World"
的操作时,viewModel的变量的didSet将触发可选函数updateBlock
。由于此功能已由自定义视图AwesomeView
设置,因此它将自动更新该视图中标签中的文本。
我觉得这很酷,很容易理解,但这有什么副作用吗,或者我没有看到什么?这是不好的做法吗?它似乎比使用具有所有功能和观察等的标准KVO容易得多。
我想将其用于UITableViewCell
的,其中bindViewModel()
将在cellForRowAtIndexPath
中调用。我不认为有任何保留周期或其他什么,但我不太善于发现它们。。
在将这种方法应用于我的应用程序之前,我希望能了解它的优缺点,但却发现这是世界上最愚蠢的想法。但对我来说似乎还可以。
我认为学习RxSwift是个好主意。然而,与此同时,你所做的大部分都很好。这种方法的局限性在于,您只能进行一次回调;RxSwift将允许任意数量的订阅一个Observable。我提前道歉,这个答案是基于公平的意见。
我会将updateBlock
更改为将"观察到的"值作为参数,即
var updateBlock: ((String) -> ())?
因为这有助于避免保留周期,并可能重命名updateBlock
以更准确地反映哪个值发生了变化,即
var awesomeTextUpdate: ((String) -> ())?
然而,@rmaddy是对的,这将成为一种痛苦,你可能希望观察到多种属性。这可以通过属性及其值的枚举来部分解决,例如
class AwesomeViewModel {
enum Property {
case awesomeText(String)
case awesomeNumber(Int)
}
}
并将CCD_ 15更改为类型
var updateBlock: ((AwesomeViewModel.Property) -> ())?
CCD_ 16到
var awesomeText: String {
didSet {
updateBlock?(.awesomeText(awesomeText))
}
}
然后客户端可以在Property
枚举上简单地switch
;在他们不关心的地方中断并访问他们关心的地方。如果你有许多潜在的可观察属性,但客户端在特定情况下只需要一个,这可能会变得很痛苦。
如果看起来没有正确的妥协,那主要是因为没有,RxSwift可能是更好的长期解决方案。然而,如果你不想广泛复制这种范式,那么一个可选的功能就可以了。记住:不要重复自己。
tl;博士,你所做的看起来很合理,但规模不大。