如何使用 RxSwift 运算符一次 select 所有项目?
How can I select all item at once using RxSwift operator?
假设我有 table 展示产品的观点。
这是我的视图模型:
ProductViewModel.swift
var selectedObserver: AnyObserver<Product>
var state: Driver<Set<Product>>
var selectedSubject = PublishSubject<Product>()
self.selectedObserver = selectedSubject.asObserver()
self.state =
selectedSubject.asObservable()
.scan(Set()) { (acc: Set<Product>, item: Product) in
var acc = acc
if acc.contains(item) {
acc.remove(item)
} else {
acc.insert(item)
}
return acc
}
.startWith(Set())
.asDriver(onErrorJustReturn: Set())
self.isSelectedAll = Driver
.combineLatest(
self.state.map { [=11=].count },
envelope.map { [=11=].products.count })
.debug()
.map { [=11=].0 == [=11=].1 }
如你所见,每次我select一个对象,我都会scan
它进入状态可观察,这样细胞就可以观察到状态变化。
这是 cell 和 viewModel 之间的 RxSwift 绑定:
ProductViewController.swift
self.viewModel.deliveries
.drive(self.tableView.rx.items(cellIdentifier: DeliveryTableViewCellReusableIdentifier, cellType: DeliveryTableViewCell.self)) { (_, item, cell) in
cell.bind(to: self.viewModel.state, as: item)
cell.configureWith(product: item)
}
.disposed(by: disposeBag)
self.tableView.rx.modelSelected(Product.self)
.asDriver()
.drive(self.viewModel.selectedObserver)
.disposed(by: disposeBag)
ProductCell.swift
func bind(to state: Driver<Set<Product>>, as item: Product) {
state.map { [=13=].contains(item) }
.drive(self.rx.isSelected)
.disposed(by: rx.reuseBag)
}
嗯,到目前为止一切顺利。
现在我的问题是如何进行 select 全部操作,例如点击 select 全部按钮,以便所有产品都以某种方式 scan
进入状态?
当然,还有更多方法可以做到这一点。我想到的一个是 single selection
和 select all
有两个不同的事件,将它们统一在一个枚举中,例如。 SelectionEvent
,合并它们并将其传递给 scan
,以便在扫描方法中可以区分它们。
粗略示例,遵循您的代码:
var selectedObserver: AnyObserver<Product>
var state: Driver<Set<Product>>
var selectedSubject = PublishSubject<Product>()
var selectedAllSubject = PublishSubject<Product>() // Added
var selectedAllObserverObserver: AnyObserver<Void> // Added
self.selectedObserver = selectedSubject.asObserver()
self.selectedAllObserverObserver = selectedAllSubject.asObserver()
enum SelectionEvent {
case product(Product)
case all([Product])
}
self.state = Observable.of(
selectedSubject.map { SelectionEvent.product([=10=]) },
// I figured envelope is observable containing all products.
selectedAllSubject.withLatestFrom(envelope.map { [=10=].products }).map { SelectionEvent.all([=10=]) }
).merge()
.scan(Set()) { (acc: Set<Product>, event: SelectionEvent) in
var acc = acc
// now you can differentitate between events
switch event {
case .product:
if acc.contains(item) {
acc.remove(item)
} else {
acc.insert(item)
}
case .all(let all):
acc = all
}
return acc
}
.startWith(Set())
.asDriver(onErrorJustReturn: Set())
希望对您有所帮助。
假设我有 table 展示产品的观点。
这是我的视图模型:
ProductViewModel.swift
var selectedObserver: AnyObserver<Product>
var state: Driver<Set<Product>>
var selectedSubject = PublishSubject<Product>()
self.selectedObserver = selectedSubject.asObserver()
self.state =
selectedSubject.asObservable()
.scan(Set()) { (acc: Set<Product>, item: Product) in
var acc = acc
if acc.contains(item) {
acc.remove(item)
} else {
acc.insert(item)
}
return acc
}
.startWith(Set())
.asDriver(onErrorJustReturn: Set())
self.isSelectedAll = Driver
.combineLatest(
self.state.map { [=11=].count },
envelope.map { [=11=].products.count })
.debug()
.map { [=11=].0 == [=11=].1 }
如你所见,每次我select一个对象,我都会scan
它进入状态可观察,这样细胞就可以观察到状态变化。
这是 cell 和 viewModel 之间的 RxSwift 绑定:
ProductViewController.swift
self.viewModel.deliveries
.drive(self.tableView.rx.items(cellIdentifier: DeliveryTableViewCellReusableIdentifier, cellType: DeliveryTableViewCell.self)) { (_, item, cell) in
cell.bind(to: self.viewModel.state, as: item)
cell.configureWith(product: item)
}
.disposed(by: disposeBag)
self.tableView.rx.modelSelected(Product.self)
.asDriver()
.drive(self.viewModel.selectedObserver)
.disposed(by: disposeBag)
ProductCell.swift
func bind(to state: Driver<Set<Product>>, as item: Product) {
state.map { [=13=].contains(item) }
.drive(self.rx.isSelected)
.disposed(by: rx.reuseBag)
}
嗯,到目前为止一切顺利。
现在我的问题是如何进行 select 全部操作,例如点击 select 全部按钮,以便所有产品都以某种方式 scan
进入状态?
当然,还有更多方法可以做到这一点。我想到的一个是 single selection
和 select all
有两个不同的事件,将它们统一在一个枚举中,例如。 SelectionEvent
,合并它们并将其传递给 scan
,以便在扫描方法中可以区分它们。
粗略示例,遵循您的代码:
var selectedObserver: AnyObserver<Product>
var state: Driver<Set<Product>>
var selectedSubject = PublishSubject<Product>()
var selectedAllSubject = PublishSubject<Product>() // Added
var selectedAllObserverObserver: AnyObserver<Void> // Added
self.selectedObserver = selectedSubject.asObserver()
self.selectedAllObserverObserver = selectedAllSubject.asObserver()
enum SelectionEvent {
case product(Product)
case all([Product])
}
self.state = Observable.of(
selectedSubject.map { SelectionEvent.product([=10=]) },
// I figured envelope is observable containing all products.
selectedAllSubject.withLatestFrom(envelope.map { [=10=].products }).map { SelectionEvent.all([=10=]) }
).merge()
.scan(Set()) { (acc: Set<Product>, event: SelectionEvent) in
var acc = acc
// now you can differentitate between events
switch event {
case .product:
if acc.contains(item) {
acc.remove(item)
} else {
acc.insert(item)
}
case .all(let all):
acc = all
}
return acc
}
.startWith(Set())
.asDriver(onErrorJustReturn: Set())
希望对您有所帮助。