RxSwift+NotificationCenterの購読機能を使ってキーボードが被らない機能を実装する
概要
今回はNotificationCenterのRxSwiftについて解説していきます。
このページをみるとNotificationCenterにはnotification
のfunctionがあることがわかります。
ということは
NotificationCenter.default.rx.notification("", object: nil)
という書き方になります。こちらはnotificationの引数を省略した形になります。
わざわざなんでNotificationをRxSwiftぽく書くんだ!という意見はあると思いますが後で説明します。
NotificationCenterの基本形
では、まずはNotificationCenterの購読の基本形を書きます。
ViewController.swift
NotificationCenter.default.rx.notification(name, object: nil) .subscribe(onNext: { notification in // 処理内容 }) .disposed(by: disposeBag)
このようになります。
これはRxSwiftを使わないやり方だと
ViewController.swift
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 購読の登録 NotificationCenter.default.addObserver(self, selector: Selector(("sample:")), name: NSNotification.Name(rawValue: "SampleNotification"), object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 購読の解除 NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "SampleNotification"), object: nil) } @objc private func sample(notification: Notification) { // 処理内容 }
と同じ実装になります。
これだとRxSwiftを使った方が購読の解除のコードが要らない分、簡単に書けますね。
今回実装する機能について
今回はラインだったりメッセンジャーアプリに付き物のキーボードを表示したときにLabel
やTextField
が被らないようにする実装について見ていきます。
RxSwiftを使わなくてもできるやん!というツッコミは勘弁してください。
よく使う機能をRxSwift で書くとどうなるかを学ぶ方がコードの書き方が分かると思います。
cocoapodについて
念の為、podの環境についておさらいします。
cocoapod
# Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'RxSwitch' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for RxSwitch pod 'RxSwift', '~> 4.0' # 追加する pod 'RxCocoa', '~> 4.0' # 追加する target 'RxSwitchTests' do inherit! :search_paths # Pods for testing end target 'RxSwitchUITests' do inherit! :search_paths # Pods for testing end end
このように
- RxSwift
- RxCocoa
のみインストールします。
storyboardの構成について
では今度はstoryboardの構成について見ていきます。
ViewControllerにはUILabel
とUITextField
のみを置きます。
UILabel
は@IBOutlet
の接続をしておきます。
UITextField
も同様です。
今回はキーボードとの関係性の実装でUITextField
の制約が重要になりますのでこちらの設定だけ確認しておきます。
これで準備が完了します。
ソースコードについて
今回はキーボードの対応ということもありますので少しコードが多くなります。
本当はもっと簡単な機能が具体例にいいのですが、今時のiOS開発でNotificationが必要になる箇所はプッシュ通知だったりキーボード対応だったりします。
ですので今回でまとめて解説します。
ViewController.swift
import UIKit import RxCocoa import RxSwift class ViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBOutlet weak var textField: UITextField! private let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() // UITextField とUILabelのtextのデータを紐づける textField.rx.text .bind(to: label.rx.text) .disposed(by: disposeBag) // キーボードが表示される時のNotificationを登録する NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification, object: nil) .subscribe({ notification in if let element = notification.element { self.keyboardWillShow(element) } }) .disposed(by: disposeBag) // キーボードが消える時のNotificationを登録する NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification, object: nil) .subscribe({ notification in if let element = notification.element { self.keyboardWillHide(element) } }) .disposed(by: disposeBag) } /// キーボードが表示時に画面をずらす。 private func keyboardWillShow(_ notification: Notification) { guard let rect = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue, let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return } UIView.animate(withDuration: duration) { let transform = CGAffineTransform(translationX: 0, y: -(rect.size.height)) self.view.transform = transform } print("keyboardWillShowを実行") } /// キーボードが降りたら画面を戻す private func keyboardWillHide(_ notification: Notification) { guard let duration = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? TimeInterval else { return } UIView.animate(withDuration: duration) { self.view.transform = CGAffineTransform.identity } print("keyboardWillHideを実行") } }
このようになります。
これで実機でビルドしてみてUITextField
をタップしてみましょう。
無事にキーボードが表示されてUITextField
が被らないことが確認できたら成功です。