Как я могу узнать, выбрано ли табличное представление в качестве наблюдаемого <Bool>? - PullRequest
1 голос
/ 29 июня 2019

В моем приложении у меня есть табличное представление с множеством строк.Представление таблицы заполняется наблюдаемой.Я делаю это, используя RxDataSources, используя RxTableViewSectionedAnimatedDataSource.

. Строки табличного представления представляют некоторые элементы, над которыми пользователь может выполнять действия.Это означает, что я хочу, чтобы некоторые кнопки появлялись, когда выбрана строка, и я хочу, чтобы эти кнопки исчезали, когда ни одна строка не выбрана.Эти кнопки представляют собой действия, которые пользователь может выполнять над элементами.

Я подумал, что могу наблюдать свойство indexPathForSelectedRow и связать его с button.rx.isHidden, например:

[actionButton1, actionButton2, actionButton3].forEach { (button) in
    button?.isHidden = true // they are always hidden initially
    self.tableView.rx.observe(IndexPath?.self, "indexPathForSelectedRow")
        .map { ($0 as? IndexPath) == nil } // $0 is a double optional, so I unwrap it like this
        .bind(to: button!.rx.isHidden)
        .disposed(by: disposeBag)
}

Однако, когда я выбираю элемент, кнопки действий не появляются вообще.

Я также пытался наблюдать indexPathsForSelectedRows, но это дает тот же результат.

Затем я пыталсяподписаться на itemSelected и itemDeselected по отдельности:

[actionButton1, actionButton2, actionButton3].forEach { (button) in
    button?.isHidden = true

    self.tableView.rx.itemSelected.subscribe(onNext: {
        _ in
        button?.isHidden = false
    }).disposed(by: disposeBag)
    self.tableView.rx.itemDeselected.subscribe(onNext: {
        [weak self] _ in
        button?.isHidden = (self?.tableView.indexPathForSelectedRow ?? nil) == nil
    }).disposed(by: disposeBag)
}

На этот раз, когда я выбираю строку, кнопки отображаются правильно.

Однако, когда изменяется наблюдаемый источник данных, такиечто выбранная строка удаляется, в результате чего в табличном представлении больше не остается выбранных строк, кнопки действий не исчезают .

Как сделать так, чтобы мои кнопки действий исчезаликогда в табличном представлении нет выбранных строк?

Обратите внимание, что мне все равно , какая строка выбрана.Я просто хочу знать , выбрана ли какая-либо строка.Другими словами, Observable<Bool>, который выдает новое значение всякий раз, когда выражение «в табличном представлении есть хотя бы одна выбранная строка» изменяется с true на false или с false на true.

1 Ответ

2 голосов
/ 29 июня 2019

Если вы используете только RxCocoa, сделайте это:

let itemIsSelected = Observable.merge(
    tableView.rx.itemSelected.map { _ in true },
    tableViewItems.map { _ in false } // this is the magic you are missing.
)
.startWith(false)

// a much easier way to handle your buttons' hidden state.
for buttonIsHidden in actionButtons.map({ $0.rx.isHidden }) {
    itemIsSelected
        .map { !$0 }
        .bind(to: buttonIsHidden)
        .disposed(by: disposeBag)
}

Если вы используете RxDataSources, решение более сложное:

let itemSelected = tableView.rx.modelSelected(String.self) // or whatever the type is.
let itemIsSelected = Observable.merge(
    itemSelected.map { _ in true },
    tableViewItems.filter(if: itemSelected) { !$0.contains($1) }
        .map { _ in false }
)
.startWith(false)

for buttonIsHidden in actionButtons.map({ $0.rx.isHidden }) {
    itemIsSelected
        .map { !$0 }
        .bind(to: buttonIsHidden)
        .disposed(by: disposeBag)
}

Вышесказанное использует мою суть: https://gist.github.com/danielt1263/1a70c4f7b8960d06bd7f1bfa81802cc3

Который содержит эту функцию:

extension ObservableType {

    /// Filters the source observable sequence using a trigger observable sequence.
    /// Elements only go through the filter when the trigger has not completed and
    ///   its last element produces a true value from the pred. If either source or trigger error's, then the source errors.
    ///
    /// - Parameters:
    ///   - trigger: The sequence to compare with.
    ///   - pred: The predicate function to determine if the element should pass through.
    /// - Returns: An Observable of the same type that passed the filter test.
    func filter<O>(if trigger: O, _ pred: @escaping (Element, O.Element) -> Bool) -> Observable<Element> where O: ObservableType {
        return self.withLatestFrom(trigger) { ($0, $1) }
            .filter { pred($0.0, $0.1) }
            .map { $0.0 }
    }
}
...