RxSwiftでキーボードの高さを取得する
RxSwiftでキーボードの高さを監視して処理したいということがあったので、気温も寒くなり色々と億劫になってしまいそうなところを気分は常夏で記事を書いていきたいと思います。
RxSwift
Rxは、Observableストリームから値やその他のイベントをブロードキャストおよびサブスクライブできるもの。具体的には、UIイベント受け取ったり、Web API レスポンスを受け取ったり、プロパティの変化を監視することが可能です。
RxSwiftは数あるReactiveXファミリーのひとつで、Swift用に実装されたRxライブラリになります。
この記事ではRxSwiftの詳細な説明やインストール方法などについては行いませんのでご了承下さい。RxSwiftの公式ドキュメントを見ていただければと思います。
開発環境
- Xcode 13
- Swift 5.5
- RxSwift 6.2.0
- RxCocoa 6.2.0
デモ
キーボードの高さを取得して、キーボードでサーフィンするようなものを作ってみました。
キーボード閉 | キーボード開 |
---|---|
RxSiwftでキーボードの高さを取得する
まずはNotificationCenter
でキーボードの高さを取得するメソッドを作成しました。
import UIKit import RxSwift extension NotificationCenter { func keyboardHeight() -> Observable<CGFloat> { return Observable .from([ self.rx.notification(UIResponder.keyboardWillShowNotification) .map { notification -> CGFloat in (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height ?? 0 }, self.rx.notification(UIResponder.keyboardWillHideNotification) .map { _ -> CGFloat in 0 } ]) .merge() } }
from
Summary:
Arrayを観測可能なシーケンスに変換する。
サマリーにもある通り、配列の要素を観測可能なものに変換しています。
UIResponder.keyboardFrameEndUserInfoKey
スクローン上でキーボードがアニメーションした後の長方形を確認するCGRectを含むNSValueで、その長方形フレームは現在のデバイスの向きの影響を受けます。
つまりはキーボードが出現した時のキーボードのCGRect
を持っているNSValue
でここからキーボードの高さを取得します。
// NSValueの持っているCGRectから高さを取得 notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height
merge
merge()
は複数の非同期処理を並列に実行することができます。つまり、Observable の渡される順番などが関係なく処理が早く終わった順にストリームに流れます。
下記画像はReactiveX - Mergeから引用させていただきました。
上部の2本のストリームがmerge
されることで下部の1本のストリームになっており、処理が早く終わった順番に流れてきているのが分かると思います。
キーボードの高さを監視して処理を行う
今回でいうと、
from
で観測可能なシーケンスとなったキーボードが表示された時の高さと非表示になった時の高さがマージされ順番にストリームに流れてきます。
その流れてきた値を使って、何らかの処理を行うことができます。
NotificationCenter.default.keyboardHeight() .observeOn(MainScheduler.instance) .subscribe(onNext: {[weak self] keyboardHeight in // キーボードの高さを使って何かをする }) .disposed(by: disposeBag)
キーボードでサーフィンをする
import UIKit import RxSwift class ViewController: UIViewController { @IBOutlet weak private var surferBotomConstraint: NSLayoutConstraint! private let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.keyboardHeight() .observe(on: MainScheduler.instance) .subscribe(onNext: { [weak self] keyboardHeight in if keyboardHeight == 0 { self?.surferBotomConstraint.constant = 0 } else { self?.surferBotomConstraint.constant += keyboardHeight } }) .disposed(by: disposeBag) } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { view.endEditing(true) } }
今回はKeyboardHeight
が新しい値に変わった時に、サーファーのBottomConstraint
にキーボードの高さ分加えてあげて、波乗りならぬキーボード乗りを表現しています。
本来ならば、アニメーションも加えてスムースなキーボード乗りを表現した方がより良いですが今回は割愛させていただきます。
RxSwiftを用いたものではないですが、僕の過去の記事でキーボードの高さ×アニメーション使った表現の記事は書いているので良ければ見てあげてください。
おわりに
「そこの君、波じゃなくてキーボードに乗らないかい?」
クラスメソッドでは働く仲間を募集しています。