根据 UITextField 中的值禁用按钮只能工作一次(RxSwift)

Posted

技术标签:

【中文标题】根据 UITextField 中的值禁用按钮只能工作一次(RxSwift)【英文标题】:Disabling a button based on value in a UITextField only works once (RxSwift) 【发布时间】:2019-05-31 13:13:06 【问题描述】:

我正在尝试处理RxCocoa,但遇到了一个与我尝试实现的一些动态 UI 行为相关的异常错误。

我有一个用于用户输入的UITextField。将输入添加到 Realm 数据库的按钮绑定到 RxSwift 操作。这绝对没问题。

最初,我禁用了该按钮,直到 UITextField 中出现至少 1 个字符长度的文本 - 此代码工作正常。当我添加对 Action 的 executionObservables 参数的订阅时,我的代码中出现了该错误,该参数应在按下按钮后清除 UITextField。

预期行为:

无文本(初始状态)> 按钮已禁用 文本输入 > 按钮启用 已输入文本并按下按钮 > 已清除文本字段并禁用按钮

实际行为:

无文本(初始状态)> 按钮已禁用 文本输入 > 按钮启用 已输入文本并按下按钮 > 已清除文本字段但按钮仍处于启用状态

添加 debug() 表明绑定到 UITextField 禁用按钮已被释放,但我不知道为什么 UIViewController 及其关联的视图模型仍应在范围内。谁能指出我正确的方向?

代码sn-p:

func bindViewModel() 
    // populate table
    viewModel.output.sectionedObservations
        .drive(tableView.rx.items(dataSource: dataSource))
        .disposed(by: disposeBag)

    // only allow enable button when there is text in the textfield
    observationTextField.rx.text
        .debug()
        .map  $0!.count > 0 
        .bind(to: addObservationButton.rx.isEnabled)
        .disposed(by: disposeBag)

// clear textfield once Action triggered by button press has completed
viewModel.addObservation.executionObservables
    .subscribe( [unowned self] _ in
        self.observationTextField.rx.text.onNext("")
)
.disposed(by: disposeBag)

// add Observation to Realm using Action provided by the view model
addObservationButton.rx.tap
    .withLatestFrom(observationTextField.rx.text.orEmpty)
    .take(1)
    .bind(to: viewModel.addObservation.inputs)
    .disposed(by: disposeBag)

【问题讨论】:

除了你的问题 - 这个$0!.count > 0 是一个等待发生的崩溃。将其替换为安全操作,例如 . ($0?.count ?? 0) > 0 我知道,但感谢您强调。这是快速的实验性代码,肯定需要重构以处理可怕的可选代码! 如果我理解正确,您最终需要 viewModel 告诉 observationTextField 它的值应该是什么。我养成了为 viewModel 的每个输出创建 Driver 的习惯,然后将其绑定到您的文本字段。用它来替换你的viewModel.addObservation.executionObservables 我确实需要以编程方式修改 UITextField 的内容,但只是将其删除,所以我不会认为我需要来自视图模型的直接输入。使用 executionObservables 可确保 Action 已正确执行,并且由于修改是将 .text 属性设置为“”,我认为最好使用简单的方法 【参考方案1】:

我认为对于ControlProperty trait 的行为方式存在一些误解。我们来看看具体的行为是Programmatic value changes won't be reported

订阅后这个 Observable observationTextField.rx.text 不会为两者发出事件:

self.observationTextField.rx.text.onNext("")

self.observationTextField.text = ""

我对您的代码有 2 条建议:

1) 手动完成工作:

viewModel.addObservation.executionObservables
    .subscribe( [unowned self] _ in
        self.observationTextField = ""
        self.addObservationButton.isEnabled = false
)
.disposed(by: disposeBag)

2) 再添加一个 Observable 和订阅:

//a
    viewModel.addObservation.executionObservables
      .map  _ in return "" 
      .bind(to: observationTextField.rx.text)
      .disposed(by: disposeBag)

    viewModel.addObservation.executionObservables
      .map  _ in return false 
      .bind(to: addObservationButton.rx.isEnabled)
      .disposed(by: disposeBag)
//b
    let executionObservables = viewModel.addObservation
      .executionObservables
      .share()

    executionObservables
      .map  _ in return "" 
      .bind(to: observationTextField.rx.text)
      .disposed(by: disposeBag)

    executionObservables
      .map  _ in return false 
      .bind(to: addObservationButton.rx.isEnabled)
      .disposed(by: disposeBag)

不确定Action 是如何实现的,为了防止工作完成两次,也许你必须共享资源。

【讨论】:

感谢指导谢尔盖。这成功了,现在我明白我做错了什么(希望再次犯这个错误!) 快速思考:在这种情况下,.do 不是首选 .subscribe(onNext:),因为我们正在执行 UI 副作用而不是更改流中的元素? UI 的手动更新比其他版本更紧凑(即使有额外的行),也同样富有表现力。我现在有了预期的行为。再次感谢 就我而言,我都可以接受:swift viewModel.addObservation.executionObservables .do(onNext: _ in self.observationTextField = "" self.addObservationButton.isEnabled = false ) .subscribe() .disposed(by: disposeBag) swift viewModel.addObservation.executionObservables .subscribe( [unowned self] _ in self.observationTextField = "" self.addObservationButton.isEnabled = false ) .disposed(by: disposeBag) 我个人更喜欢订阅关闭中的代码。

以上是关于根据 UITextField 中的值禁用按钮只能工作一次(RxSwift)的主要内容,如果未能解决你的问题,请参考以下文章

禁用iOS键盘中的输入按钮

启用/禁用 UIButton 取决于 UITextField 中的文本长度

根据标签的值禁用按钮

根据文本控件值标记和启用/禁用连续表单上的按钮

使用委托使用按钮更改 tableViewCell 内 UITextField 的值

如何根据垫单选按钮的值禁用垫输入字段?