如何使用RxSwift运算符一次选择所有项目?

时间:2018-03-15 09:58:15

标签: swift rx-swift

假设我有一个表格视图来展示产品。

这是我的viewModel:

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 { $0.count },
            envelope.map { $0.products.count })
        .debug()
        .map { $0.0 == $0.1 }

如你所见,每当我选择一个物体时,我会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 { $0.contains(item) }
    .drive(self.rx.isSelected)
    .disposed(by: rx.reuseBag)
}
好吧,到目前为止一切顺利。

现在我的问题是如何选择所有操作,例如点击全选按钮,以便所有产品以某种方式 scan 进入州?

1 个答案:

答案 0 :(得分:1)

当然,还有更多方法可以做到这一点。我想到的是single selectionselect 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($0) },
            // I figured envelope is observable containing all products.
            selectedAllSubject.withLatestFrom(envelope.map { $0.products }).map { SelectionEvent.all($0) }
        ).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())

我希望这会有所帮助。