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
- 安全解包可选字符串
validate
returns Single<Bool>
在我的例子中,所以我把它变成 Observable
。请注意,我故意使用 map
而不是 flatMap
以便使用 switchLatest
在步骤 3 中提供的功能
- 我合并 a 和 b 以创建一个
Observable<Observable<Bool>>
,switchLatest
允许我在用户再次开始输入时忽略 validate
结果。 distinctUntilChanged
丢弃重复 false
s
我有一个要验证的文本字段,我想在用户输入时禁用一个按钮。用户停止输入后(去抖动 1 秒),将执行验证并根据结果有条件地启用按钮。请注意当用户只输入一个字符时的特殊情况,验证仍然会发生。
--"a"-"ab"-"abc"------------------"ab"--"a"------------------"ab"-----------------
--false---------validate("abc")---false----validate("a")-----false--validate("ab")
This SO (
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
- 安全解包可选字符串
validate
returnsSingle<Bool>
在我的例子中,所以我把它变成Observable
。请注意,我故意使用map
而不是flatMap
以便使用switchLatest
在步骤 3 中提供的功能
- 我合并 a 和 b 以创建一个
Observable<Observable<Bool>>
,switchLatest
允许我在用户再次开始输入时忽略validate
结果。distinctUntilChanged
丢弃重复false
s