RxSwift и AlertAction

#swift #rx-swift #uialertaction

#swift #rx-swift #uialertaction

Вопрос:

У меня есть Tableview с UIAlertAction. Когда я нажимаю на ячейку, появляется всплывающее окно, а затем я могу решить, какую операцию выполнить. Во время операции Ok (нажатие кнопки) Я хочу наблюдать за данными ячейки и получать их в ViewModel. Но есть проблемы. Когда я нажимаю в первый раз, значения нет, и если я просто щелкаю ячейку, данные все равно отправляются в ViewModel. Если я нажимаю больше всплывающих окон UIAlertAction, отправленные данные увеличиваются. Например: первый щелчок — ничего, второй щелчок — значение 1, значение третьего щелчка 1, 1, 1 и так далее. Как начать наблюдение за ячейками с первого щелчка всплывающего окна UIAlertAction и как получить только пример данных?

ViewController:

 func bindTableView() {
    viewModel.stationItems.bind(to: addTableView.rx.items(cellIdentifier: "addCell", cellType: AddTableViewCell.self)) { (row, item, cell)
        in
        cell.cellAdd = item
    }.disposed(by: disposeBag)
    
    addTableView.rx.modelSelected(StationItem.self)
        .subscribe(onNext: { item in

            let alert = UIAlertController(title: "Add Station", message: "Do you want to add a station to your favorites?", preferredStyle: .alert)
            let ok = UIAlertAction(title: "Ok", style: .default, handler: { action in
                self.addTableView.rx.modelSelected(StationItem.self)
                    .bind(to: self.viewModel.stationItem)
                    .disposed(by: self.disposeBag)
                self.viewModel.addStationItem()
            })
            alert.addAction(ok)
            let cancel = UIAlertAction(title: "Cancel", style: .default, handler: { action in
            })
            alert.addAction(cancel)
            DispatchQueue.main.async(execute: {
                self.present(alert, animated: true)
            })
        }).disposed(by: disposeBag)
    
    viewModel.fetchStations()
}
 

ViewModel:

  let stationItem = PublishSubject<StationItem>()

 func addStationItem() {
    stationItem.subscribe(onNext: {(data) in
        print("Data: (data)")
        
        })
        .disposed(by: disposeBag)
    
    print("ADD:")
            
}
 

Ответ №1:

Проблема заключается в вашей ретроактивной подписке на modelSelected . Имейте в виду, что каждый раз, когда вы вызываете функцию, вы получаете новую наблюдаемую. С момента подписки он будет сообщать вам всякий раз, когда что-то нажимается, но он не сообщит вам о предыдущем нажатии, которое вызвало появление предупреждения в первую очередь.

Первое, что нужно сделать здесь, — это создать повторно используемую функцию, которая позволит вам создавать и представлять представления предупреждений. Всякий раз, когда вы хотите попросить пользователя о чем-то, используйте такой шаблон:

 extension UIViewController {
    func presentAlert(title: String?, message: String?) -> Observable<Void> {
        let result = PublishSubject<Void>()
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let ok = UIAlertAction(title: "OK", style: .default, handler: { _ in
            result.onNext(())
            result.onCompleted()
        })
        let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in
            result.onCompleted()
        }
        alert.addAction(ok)
        alert.addAction(cancel)
        present(alert, animated: true)
        return result
    }
}
 

Если вы используете обязательные входные данные для своей модели представления, пройдите весь путь для обеспечения согласованности:

 final class ViewModel {
    let stationItems = PublishSubject<[StationItem]>()

    func addStationItem(_ item: StationItem) {
        print("ADD:")
    }

    func fetchStations() { 
        // do your fetching and load `stationItems`.
    }
}
 

Затем в функции привязки вашего контроллера представления вы можете использовать flatMap для вызова вышеуказанного:

 addTableView.rx.modelSelected(StationItem.self)
    .flatMapFirst { [unowned self] item in
        self.presentAlert(title: "Add Station", message: "Do you want to add a station to your favorites?")
            .map { item }
    }
    .bind { [viewModel] in
        viewModel?.addStationItem($0)
    }
    .disposed(by: disposeBag)
 

То, что вы здесь делаете, очень похоже на архитектуру Binder. Возможно, вам будет полезно прочитать об этом подробнее.

Комментарии:

1. Довольно удивительное решение. Большое вам спасибо!

2. На самом деле вы можете настроить подобную систему так, чтобы она отображала все ваши контроллеры просмотра, а не только оповещения. Инструменты для этого находятся здесь: github.com/danielt1263/CLE-Architecture-Tools

3. @beginner992 Если вы найдете ответ полезным, вы должны принять его.

4. Готово! Извините за это и еще раз благодарю вас за помощь.