我有一个要验证的文本字段,我想在用户键入时禁用按钮。用户停止键入(在1秒钟后反跳)后,将执行验证,并根据结果有条件地启用按钮。请注意,当用户只键入一个字符时,仍然会发生验证。
--"a"-"ab"-"abc"------------------"ab"--"a"------------------"ab"-----------------
--false---------validate("abc")---false----validate("a")-----false--validate("ab")
此SO(Deliver the first item immediately, 'debounce' following items)在RxJava中提出了以下解决方案。但这似乎只返回第一个元素,而不是在用户反跳后再次开始输入时不返回?如果我错了请纠正我
Observable.from(items).publish(publishedItems ->
publishedItems.limit(1).concatWith(
publishedItems.skip(1).debounce(1, TimeUnit.SECONDS)
)
)
答案 0 :(得分:1)
一种选择是将问题“用户正在输入”与“输入是否有效”分开。
一种描述用户是否在打字的笨拙方式:
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) { !$0 && $1 }
.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($0) }
.bind(to: button.rx.isEnabled)
.disposed(by: disposeBag)
请注意,使用这种方法,当用户恢复输入时,将需要取消异步验证操作。
希望有人可以提出更清洁的解决方案,但是我认为这是一个不错的起点。
答案 1 :(得分:0)
经过深思熟虑,我可以通过以下方式完全解决问题,一切都按我的预期进行
let input = textField.rx.text.distinctUntilChanged()
let keystroke = input.map { _ in Observable.just(false) }
let validate = input
.flatMap { Observable.from(optional: $0) } // 1
.filter { $0.count >= minimumTextLength }
.debounce(1, scheduler: MainScheduler.instance)
.map { self.networkManager.validate($0).asObservable() } // 2
return Observable.merge(keystroke, validate).switchLatest().distinctUntilChanged() // 3
validate
返回Single<Bool>
,所以我将其转换为Observable
。请注意,我故意使用map
而不是flatMap
来使用步骤3中的switchLatest
提供的功能Observable<Observable<Bool>>
,switchLatest
允许我忽略validate
的结果(如果用户再次开始输入)。 distinctUntilChanged
丢弃重复的false
s