使用 RxSwift 在 UITableView 中进行 2 路绑定

Posted

技术标签:

【中文标题】使用 RxSwift 在 UITableView 中进行 2 路绑定【英文标题】:2 way binding in UITableView using RxSwift 【发布时间】:2019-02-09 07:43:54 【问题描述】:

我正在使用带有RxSwift、RxCocoa、RxDataSources 的 MVVM 模式。

我已经使用RxDataSource 成功地用ListViewModel 中的PaletteViewModel 数组填充了UITableView,但这是一种绑定方式。

我想实现我在图片中显示的内容,即我想将UITextFieldUITableViewCell 绑定到Observable,该Observable 存在于ListViewModel 数组中的某个索引处

我想用PaletteViewModelUITextFieldanswer 属性来做2 way binding。如果用户更改了 textField 中的文本,它应该更改 answer 属性中存在于特定索引处的值,反之亦然。

如何使用MVVM pattern 使用ReactiveX 框架来实现这样复杂的事情?

如果UITableViewCell 在某些IndexPath 处的UITableViewCell 因不可见而从内存中删除并且可观察对象的值发生更改会导致崩溃,因为IndexPath 处的UITextField 将返回nil?

【问题讨论】:

answer的类型是什么? 我想对单元格中存在的 UITextField 进行 2 路绑定,而 observable 存在于数组中。 这不能回答我的问题。如果您告诉我answer 属性的类型,我可能会给您一个解决方案。 是的,它是 BehaviorRelay 【参考方案1】:

UITextField 是一个 input 元素。您不需要对它进行双向绑定,因为您不应该动态更改它。您应该做的最多就是初始化它,您不需要为此绑定。

您没有提及此输入的最终输出是什么,因此答案可能与以下不同。此特定解决方案假定您需要将所有答案作为一个组推送到服务器或数据库。也许当一个按钮被点击时。

下面有很多代码,但它按原样编译(使用正确的导入)。您可以订阅 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

【讨论】:

哇,这太简单了。感谢您的帮助。 所以想想如果我做了 2 路绑定并且包含 textfielf 的单元格从内存中删除,如果可观察值发生更改,那么它会导致崩溃吗? 双向绑定没有任何意义。考虑一下“双向绑定”数据将采用的路径。用户在文本字段中输入数据,该数据会转到 BehaviorRelay,然后返回到文本字段......这有什么意义? 您想将数据发送到标签。 TextFields 向您发送数据。 是的,你说得通

以上是关于使用 RxSwift 在 UITableView 中进行 2 路绑定的主要内容,如果未能解决你的问题,请参考以下文章

使用 RxSwift 异步加载 UITableView 单元格

无法使用 RxSwift 变量 asObservable() 设置绑定(到:UITableView)

也使用 UISearchController 时的 RxSwift UITableView 绑定

RxSwift之UI控件UITableView扩展的基本使用

如何在 UITableView 的 RXswift 和 RXCocoa 中实现 tableview 单元格的内部?

处理 UITableView 绑定中的连接错误(Moya、RxSwift、RxCocoa)