我有一个对象的Observable数组订阅,每个对象必须绑定一个UI组件和UI组件内,我必须添加点击手势的子视图,这需要另一个订阅。我是RxSwift的新手,读了一些类似的问题和文章,但要么我不理解,要么内容不太相关。
items.subscribe(onNext: { [weak self] items in
for item in items {
let view = aView()
view.label.text = item.name
view.image.rx.touchUpInside.subscribe(onNext: { [viewModel] _ in
doSomething()
}).disposed(by: disposeBag)
self?.stackView.addArrangedSubview(view)
}
}).disposed(by: disposeBag)
我在这里读过flatMap
和一个类似的问题,但无法理解它。请解释一下你的答案,这样我可以学习处理类似的问题。
您在正确的轨道上,因为flatMap
是您想要的。您可能会感到困惑的是,您想要订阅许多Observables(在您的for循环中),而不仅仅是一个。我们先来分析一下。
对于你的数组中的每个项目,你想创建一个视图,命名它,把它添加到你的stackView,然后听touchUpInside
观察。因此,为此创建一个函数(这实际上是将for循环的主体转换为一个自由函数):
func addView(to stackView: UIStackView, for item: Item) -> Observable<Void> {
let view = aView()
view.label.text = item.name
stackView.addArrangedSubview(view)
return view.image.rx.touchUpInside
}
注意,上面是一个自由函数。不要让它成为类中的方法;它不需要self.
在重构之后,你的原始代码看起来像这样:items.subscribe(onNext: { [weak self] items in
for item in items {
addView(to: self!.stackView, for: item)
.subscribe(onNext: { [viewModel = self!.viewModel] _ in
doSomething()
})
.disposed(by: self!.disposeBag)
}
})
.disposed(by: disposeBag)
接下来,我们看到您正在对数组中的每个项目执行相同的操作,并且它们的所有订阅都将导致调用相同的doSomething()
…那么让我们来映射数组并合并observable…
func addViewToStack(from item: Item) -> Observable<Void> {
addView(to: stackView, for: item)
}
items.subscribe(onNext: { [weak self] items in
Observable.merge(items.map { addViewToStack(from: $0) })
.subscribe(onNext: { [viewModel = self!.viewModel] _ in
doSomething()
})
.disposed(by: self!.disposeBag)
})
.disposed(by: disposeBag)
注意,将addViewToStack(from:)
函数嵌入到当前函数中;不要让它成为一种方法。这样它将捕获stackView
而不捕获self。
从这里…我们看到Observable.merge(items.map(addViewToStack(from:)))
需要一个项目数组并返回一个Observable,这正是flatMap
的签名所要求的…
items
.flatMap { Observable.merge($0.map { addViewToStack(from: $0) }) }
.subscribe(onNext: { [viewModel] _ in
doSomething()
})
.disposed(by: disposeBag)
现在我们没有内部订阅了,我们也不再需要捕获self。
综合起来,结果看起来像这样:
class Example {
let stackView = UIStackView()
let viewModel = ViewModel()
let disposeBag = DisposeBag()
func example(items: Observable<[Item]>) {
func addViewToStack(from item: Item) -> Observable<Void> {
addView(to: stackView, for: item)
}
items
.flatMap { Observable.merge($0.map { addViewToStack(from: $0) }) }
.subscribe(onNext: { [viewModel] _ in
doSomething()
})
.disposed(by: disposeBag)
}
}
func addView(to stackView: UIStackView, for item: Item) -> Observable<Void> {
let view = aView()
view.label.text = item.name
stackView.addArrangedSubview(view)
return view.image.rx.touchUpInside
}