Galapagos Engineering Blog

株式会社ガラパゴス エンジニアチームによるブログです。

UITextViewを画面いっぱいのサイズにする

御機嫌よう、ガラパゴスのおとめです。

今日は、UITextViewを、オートレイアウトを使って、キーボードを除いた画面いっぱいのサイズにしてみようと思います。UIScrollViewに入れてキーボード表示時にスクロールさせるのではなく、使える広さは全部UITextViewにします。

この記事は、Swift 3.0とXcode 8.2.1を対象としています。

Viewの作成

適当にプロジェクトを作成して画面を作ります。よくある感じのUITableViewのある画面で追加ボタンを押すと入力画面を表示するイメージでViewを作成し、Text Viewを適当にドロップしたら、Add New Constraintsで上下左右を0に設定します。

f:id:glpgsinc:20170124183807p:plain

キーボードの通知を受け取る

さて、UITextViewは画面いっぱいにしていますが、このままでは下部がキーボードに隠れてしまいます。そこで、キーボードが表示された時に大きさを調整する必要があります。

キーボードに関連する通知は、NotificationCenterを通じて取得することができます。キーボード関連の通知は以下のようになっています。

通知 呼ばれるタイミング
.UIKeyboardWillShow キーボードが表示される時
.UIKeyboardDidShow キーボードが表示された後
.UIKeyboardWillChangeFrame キーボードがViewの位置やサイズを変更する時
.UIKeyboardDidChangeFrame キーボードがViewの位置やサイズを変更した後
.UIKeyboardWillHide キーボードが非表示になる時
.UIKeyboardDidHide キーボードが非表示になった後

通知を処理するには、NotificationCenter.addObserver(...)で、通知とそれを受け取る関数を指定します。ここではViewが表示される時に呼び出します。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let notificationCenter = NotificationCenter.default
    notificationCenter.addObserver(self, selector: #selector(self.keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
    notificationCenter.addObserver(self, selector: #selector(self.keyboardWillChangeFrame(notification:)), name: .UIKeyboardWillChangeFrame, object: nil)
    notificationCenter.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: .UIKeyboardWillHide, object: nil)
}

もちろん.addObserver(...)したからにはお行儀よく.removeObserver(...)しましょう。Viewが表示される時に.addしたのですから、その反対といえばViewが消える時ですね。nameを指定しなければ、全ての通知を解除します。

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    let notificationCenter = NotificationCenter.default
    notificationCenter.removeObserver(self)
}

UITextViewの制約を変更する

今回はオートレイアウトを使っていますので、キーボードが表示される時に、下の余白をキーボードの高さに合わせることで実現します。

まず、Assistant Editorを表示して、ドキュメントアウトラインのConstraintsから.bottomをCtrlキーを押しながらEditorにドロップします。

@IBOutlet weak var todoConstraint: NSLayoutConstraint!

これでコードから制約を変更することができるようになります。では関数を実装しましょう。キーボードの高さを取得して、制約にその値を設定します。レイアウトを変更しますので、最後にUIView.layoutIfNeeded()を呼び出します。

func keyboardWillShow(notification: Notification) {
    let rect = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let duration: TimeInterval = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! Double
    self.todoConstraint.constant = rect.height
    UIView.animate(withDuration: duration, animations: { () -> Void in
        self.view.layoutIfNeeded()
    })
}

キーボードの種類を切り替えたり変換候補が最初に表示される場合などでキーボードの高さが変わる場合でも、.UIKeyboardWillShow通知が発火します。

反対にキーボードが非表示になる際には、制約に0を入れます。

では実行してみましょう。UITextViewをタップしてみると、(スクリーンショットでは分かりにくいですが)キーボードを除いた高さになりましたね?

f:id:glpgsinc:20170124184056p:plain

さいごに

最近Swiftに入門したのですが、入門前はキーボード表示でViewのサイズを変更するなんて簡単というか自動でできそう? くらいの勢いでした。こうして記事にして見ると実際簡単そうですが、実は意外とハマったり?

さて、ガラパゴスでは人類の進化に貢献したいエンジニアを大絶賛超募集しています。皆様の応募をお待ちしています。

www.glpgs.com

では、御機嫌よう。

この記事は業務の一環として業務時間を使って書きました