RxSwift:立即交付第一个项目,去抖后续项目

RxSwift: Deliver the first item immediately, debounce following items

我有一个要验证的文本字段,我想在用户输入时禁用一个按钮。用户停止输入后(去抖动 1 秒),将执行验证并根据结果有条件地启用按钮。请注意当用户只输入一个字符时的特殊情况,验证仍然会发生。

--"a"-"ab"-"abc"------------------"ab"--"a"------------------"ab"-----------------

--false---------validate("abc")---false----validate("a")-----false--validate("ab")

This SO () 在 RxJava 中提出了以下解决方案。但似乎只有 returns 第一个元素,而不是当用户在去抖动后再次开始输入时?如果我错了请纠正我

Observable.from(items).publish(publishedItems -> 
    publishedItems.limit(1).concatWith(
        publishedItems.skip(1).debounce(1, TimeUnit.SECONDS)
    )
)

一种选择是将问题 "is the user typing" 与 "is the input valid."

分开

描述用户是否正在输入的一种(有点笨拙的)方式:

let isTyping = PublishSubject<Bool>()

textField.rx.text
    .map { _ in true }
    .bind(to: isTyping)
    .disposed(by: disposeBag)

textField.rx.text
    .debounce(1.0, scheduler: MainScheduler.instance)
    .map { _ in false}
    .bind(to: isTyping)
    .disposed(by: disposeBag)

然后,您可以这样描述按钮启用状态:

isTyping.withLatestFrom(isValid) { ![=11=] &&  }
            .bind(to: button.rx.isEnabled)

当然,要让这种方法在用户开始输入时立即生效,isValid 最好以一个值开头。

您也可以通过以下方式简化它。如果这些值只在本地使用,这可能没问题。

textField.rx.text
    .map { _ in false }
    .bind(to: button.rx.isEnabled)
    .disposed(by: disposeBag)

textField.rx.text
    .debounce(1.0, scheduler: MainScheduler.instance)
    .flatMap { [weak self] in self?.validate([=12=]) }
    .bind(to: button.rx.isEnabled)
    .disposed(by: disposeBag)

请注意,使用此方法时,需要在用户继续输入时取消异步验证操作。

希望有人可以提出更简洁的解决方案,但我认为这是一个不错的起点。

经过一番思考,我终于通过以下方式完全解决了,一切如我所愿

    let input = textField.rx.text.distinctUntilChanged()
    let keystroke = input.map { _ in Observable.just(false) }
    let validate = input
        .flatMap { Observable.from(optional: [=10=]) }  // 1  
        .filter { [=10=].count >= minimumTextLength }      
        .debounce(1, scheduler: MainScheduler.instance)
        .map { self.networkManager.validate([=10=]).asObservable() } // 2
    return Observable.merge(keystroke, validate).switchLatest().distinctUntilChanged() // 3
  1. 安全解包可选字符串
  2. validate returns Single<Bool> 在我的例子中,所以我把它变成 Observable。请注意,我故意使用 map 而不是 flatMap 以便使用 switchLatest 在步骤 3
  3. 中提供的功能
  4. 我合并 a 和 b 以创建一个 Observable<Observable<Bool>>switchLatest 允许我在用户再次开始输入时忽略 validate 结果。 distinctUntilChanged 丢弃重复 falses