TL;DR
Stringを1文字ずつ一定間隔で、UILabelやUITextViewに流し込みたい。
「1文字ずつ」、「一定間隔で」……、これはRxが得意そうなやつだ。典型的なストリームの問題だ。
そんなこんなで実装例がなかなか見つからなかったので、考えてみました。
実装例
下記の例では0.05秒間隔で1文字ずつ流し込んでいきます。
@IBAction func didTapButton(_ sender: UIButton) {
label.text = nil
let lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
let text = Observable.just(lorem)
let timer = Observable<Int>.interval(0.05, scheduler: MainScheduler.instance)
_ = Observable.combineLatest(timer, text)
.takeWhile { (i, t) in
i < t.characters.count
}
.flatMapLatest { (i, t) -> Observable<String> in
let characters = Array(t.characters)
let character = characters[i]
return Observable.just("\(character)")
}
.subscribe(onNext: { [weak self] (character) in
let currentText = self?.label.text ?? ""
self?.label.text = currentText + character
})
}
ポイントはtakeWhile
です。
i
が文字数以上になるとcompleted
が呼ばれ、シーケンスがterminateされます。
試しにsubscribe { print($0) }
に変えてみるとcompleted
が呼ばれているのが確認できます。
next(L)
next(o)
next(r)
// ...
completed