しおメモ

雑多な技術系ブログです。ニッチな内容が多いです。

iOSのキーボードの高さと連動したビューを作る

絶対忘れる自信があるので書いておきます。

なんとなく、この辺りのインターフェースもまた変わりそうな予感がするので、暫定Swift 5iOS 13版です。

NotificationCenter(raw)

NotificationCenterはライフサイクルに合わせてobserverを破棄してくれるので、addObserverの最初の引数で適切なオブジェクトを指定してあげれば、明示的にremoveObserverを呼ぶ必要はないです。

NotificationCenter.default.addObserver(
    self,
    selector: #selector(keyboardWillChangeFrame(_:)),
    name: UIResponder.keyboardWillChangeFrameNotification,
    object: nil
)

Text Fieldをキーボードに追従させて動かす例。
相変わらずI/Fがイケてないので、いったん値を探してNSValueにキャストする必要があります。

@objc private func keyboardWillChangeFrame(_ notification: Notification) {
    guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

    let keyboardHeight = UIScreen.main.bounds.height - keyboardFrame.cgRectValue.minY
    textField.transform = CGAffineTransform(translationX: 0, y: min(0, -keyboardHeight + view.safeAreaInsets.bottom))
}

実際に使う場合はSafe Areaの分を考慮する必要があるので、上のコードのような調整をする必要があります。

CGAffineTransformや、layoutIfNeeded()を使えばいい感じのアニメーションになりますが、値は細かくは飛んでこないので、その他の方法を使う場合は、keyboardAnimationDurationUserInfoKeyなども活用します。

f:id:scior:20200215180258g:plain

RxSwift

NotificationCenterにRxが生えてるのでそれを利用します。

 let keyboardHeight: Observable<CGFloat> = NotificationCenter.default.rx.notification(UIResponder.keyboardWillChangeFrameNotification)
    .compactMap { notification -> CGFloat? in
        guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return nil }

        return UIScreen.main.bounds.height - keyboardFrame.cgRectValue.minY
    }