实现一个需求 —— 使用 RxSwift 订阅 UIView 的 isHidden 属性

最近在做项目的时候,遇到了一个需求。我有三个 View(View1, View2, View3),View1 和 View2 其中一个显示,View3 就显示。

稍微思考了一下,觉得订阅 isHidden 属性的改变,然后通过 CombineLatest 组合 Observable 来实现。

问题

而问题就来了。。。

isHidden 居然是个 UIBindingObserver<UIView, Bool>

尝试使用 KVO

我在使用 OC 时,会使用 ReactiveCocoa 。而在 ReactiveCocoa 我经常使用 KVO 去监控属性。或者在 RxSwift 也有类似的方法吧?

好像的确有类似的东西,那就到源码中查看怎么使用吧。。。

从这些注释,大概知道了使用方法,那就开干吧。。。

只打印了 next(Optional(false)), 并不能监控到 isHidden 值的变化=。=

为什么呢?难道我使用方法不对?找找可参照的例子。
KVOObservableTests.swift 找到了一段测试代码:

注意,这里的属性都使用了 dynamic , 感觉好像抓到了些什么?先尝试定义一个类试试看。

起作用了😎,那把 dynamic 删了呢?

又不好使了😔 为什么呢?
其实喵神在《Swift 开发必备 Tips》中就提过这个问题。

所以,只能另想办法了😒

使用 methodInvoked

还记得 UITextField 经常使用的 text 属性么?它又是怎么实现的呢?

查看源码,发现它使用了 UIControlvalue 方法。继续往下看。。。

这里是通过监控控件事件来实现的。。🤔那么 textField.rx.text.subscribe 同样存在一个问题

1
textField.text = "test"

这样的操作,也是没法得到事件的。。

但可以从这个段代码中得到给启发,我是否可以通过订阅方法处理来完成我想要的需求呢?我们在设置属性时必然会走 set 方法。

嗯,这个好像是我们需要的方法,那就看看能否实现吧。

好像起作用了,但我们可以做进一步的优化。我希望事件就给我返回个 Bool,而且包含一开始的默认属性。

这样就得到我想要的结果了🙃,但我可以让它变得更好用点,把它封装到 rx 中作为一个 hidden 属性好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Reactive where Base: UIView {
var hidden: Observable<Bool> {
return self.methodInvoked(#selector(setter: self.base.isHidden))
.map { event -> Bool in
guard let isHidden = event.first as? Bool else {
fatalError()
}
return isHidden
}
.startWith(self.base.isHidden)
}
}

嗯,现在就可以愉快的订阅 hidden 属性了 (^-^)V

如果大家有更好的方法,欢迎分享讨论。

最后:欢迎讨论、批评、指错。