Двухстороннее связывание в UITableView с использованием RxSwift
Я использую шаблон MVVM с RxSwift, RxCocoa, RxDataSources.
Я успешно заселил UITableView
с массивом PaletteViewModel
присутствует в ListViewModel
используя RxDataSource
но это один из способов связывания.
Я хочу достичь того, что я показал на картинке, т.е. я хочу связать UITextField
от UITableViewCell
к Observable
который присутствует в некотором индексе в массиве в ListViewModel
я хочу делать 2 way binding
с UITextField
а также answer
собственность PaletteViewModel
, Если пользователь изменяет текст в textField, он должен изменить значение в свойстве answer, присутствующем в определенном индексе, и наоборот.
Как я могу достичь чего-то сложного, используя это, используя MVVM pattern
с помощью ReactiveX
рамки?
Что делать, если UITableViewCell
некоторые IndexPath
удаляется из памяти, так как он не виден, и значение наблюдаемого изменяется, это приведет к падению, так как UITextField
при этом IndexPath
вернет ноль?
1 ответ
UITextField
является элементом ввода. Вам не нужно двухстороннее связывание с ним, потому что вы не должны динамически изменять его. Самое большее, что вы должны сделать, это инициализировать его, и вам не нужно связывание для этого.
Вы не упоминаете, какой будет конечный результат для этого ввода, поэтому ответ может отличаться от приведенного ниже. Это конкретное решение предполагает, что вам нужно отправить все ответы в виде группы на сервер или в базу данных. Может быть, когда кнопка нажата.
Ниже приведено много кода, но он компилируется как есть (с соответствующим импортом.) Вы можете подписаться на ListViewModel.answers
чтобы увидеть все ответы, собранные вместе.
class ViewController: UIViewController {
@IBOutlet weak var myTableView: UITableView!
let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let answersSubject = PublishSubject<(PaletteID, String)>()
let viewModel = ListViewModel(answersIn: answersSubject.asObservable())
viewModel.paletteViewModels
.bind(to: myTableView.rx.items(cellIdentifier: "Cell", cellType: MyCell.self)) { index, element, cell in
cell.answerTextField.text = element.initialAnswer
cell.answerTextField.rx.text.orEmpty
.map { (element.id, $0) }
.bind(to: answersSubject)
.disposed(by: cell.bag)
}
.disposed(by: bag)
}
}
class MyCell: UITableViewCell {
@IBOutlet weak var answerTextField: UITextField!
let bag = DisposeBag()
}
struct ListViewModel {
let paletteViewModels: Observable<[PaletteViewModel]>
let answers: Observable<[PaletteID: String]>
init(answersIn: Observable<(PaletteID, String)>) {
paletteViewModels = Observable.just([])
answers = answersIn
.scan(into: [PaletteID: String]()) { current, new in
current[new.0] = new.1
}
}
}
struct PaletteViewModel {
let id: PaletteID
let initialAnswer: String
}
struct PaletteID: RawRepresentable, Hashable {
let rawValue: String
}