Help us understand the problem. What is going on with this article?

UITextFieldがキーボードに隠れるのを防ぐ

More than 3 years have passed since last update.

画面下部のテキストボックス(UITextField)をタップしてキーボードが出てくる際、viewをスクロールさせてあげないとテキストボックスがキーボードの裏で隠れてしまう。iOS開発を自分でやってみて初めて知った事実。

UITextFieldを載っけているViewをスクロールさせて、これを防ぐ。

準備

まずはViewをStoryboardで普通に作っていく。Xcode7.2で試しています

スクロールさせなきゃいけないので、UIViewControllerを置いた後、UIScrollView => UIViewの順にViewを重ねていく。

AutoLayoutの設定は詳しく書かないが、上記2つのviewとも親viewの上下左右の端にピッタリくっつける。一番最後に置いたUIViewは、UIViewControllerについていたUIView(親の親view)と高さ・幅を等しくする。この最後に置いたUIViewの上にUITextFieldをセット。

↓ こんな感じのStoryboardになる。

スクリーンショット 2015-12-17 23.49.38.png

UITextFieldをセットしたUIViewのAutolayout設定は以下。

スクリーンショット 2015-12-17 23.42.41.png


基本的なviewが出来たのでシミュレータで実行。UITextFieldは隠れてしまい、何が入力されているか分からない。

Simulator Screen Shot 2015.12.17 23.51.22.png

キーボードで隠れないように

というわけで、これを防ぐ。

キーボードの表示/非表示の通知

キーボードが表示される/隠れる直前に、UIKeyboardWillShowNotification, UIKeyboardWillHideNotificationというNotificationが飛んでいるので、それをオブザーバでキャッチできるようにする。

UIScrollViewUITextFieldをOutletでコード側で利用できるようにしておく。
また、UITextFieldのデリゲートを設定し、returnキーを押したらキーボードを隠す。

ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        textField.delegate = self
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: "keyboardWillBeShown:",
            name: UIKeyboardWillShowNotification,
            object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: "keyboardWillBeHidden:",
            name: UIKeyboardWillHideNotification,
            object: nil)
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        NSNotificationCenter.defaultCenter().removeObserver(self,
            name: UIKeyboardWillShowNotification,
            object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self,
            name: UIKeyboardWillHideNotification,
            object: nil)
    }

    func keyboardWillBeShown(notification: NSNotification) {
    }

    func keyboardWillBeHidden(notification: NSNotification) {
    }

    // MARK: - UITextFieldDelegate

    func textFieldShouldReturn(textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

}

キーボードの表示 <=> 非表示の切替が可能になり、そのイベントが起こる際にkeyboardWillBeShown:, keyboardWillBeHidden:を実行するようになった。

画面のスクロール処理

続いて、本題の画面スクロール処理を書いていく。以下の様な方針。

  • UIKeyboardWillShowNotificationuserInfoにキーボードの位置情報が入っているので利用
  • キーボードの位置とUITextFieldの位置から、スクロールサイズを計算しスクロール
  • UIKeyboardWillShowNotificationには、キーボード表示時のアニメーション時間も含まれるため、その時間で画面をスクロールさせる
ViewController.swift
func keyboardWillBeShown(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardFrame = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue, animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue {
            restoreScrollViewSize()

            let convertedKeyboardFrame = scrollView.convertRect(keyboardFrame, fromView: nil)
            let offsetY: CGFloat = CGRectGetMaxY(textField.frame) - CGRectGetMinY(convertedKeyboardFrame)
            if offsetY < 0 { return }
            updateScrollViewSize(offsetY, duration: animationDuration)
        }
    }
}

func keyboardWillBeHidden(notification: NSNotification) {
    restoreScrollViewSize()
}

func updateScrollViewSize(moveSize: CGFloat, duration: NSTimeInterval) {
    UIView.beginAnimations("ResizeForKeyboard", context: nil)
    UIView.setAnimationDuration(duration)

    let contentInsets = UIEdgeInsetsMake(0, 0, moveSize, 0)
    scrollView.contentInset = contentInsets
    scrollView.scrollIndicatorInsets = contentInsets
    scrollView.contentOffset = CGPointMake(0, moveSize)

    UIView.commitAnimations()
}

func restoreScrollViewSize() {
    scrollView.contentInset = UIEdgeInsetsZero
    scrollView.scrollIndicatorInsets = UIEdgeInsetsZero
}

上記の方針にしたがって何を行っているかというと、次の通り。

  • UIKeyboardWillShowNotificationには、以下の情報が含まれているので、それを取得
    • キーボードのframeの情報(UIKeyboardFrameEndUserInfoKey)
    • キーボードのアニメーション時間(UIKeyboardAnimationDurationUserInfoKey)
  • キーボードのframeの情報は、デバイスのウインドウ全体の中での座標になっているので、convertRect(_:fromView:)scrollView内での座標に変換
  • textFieldの下端とキーボードの上端のy座標(変換後)を比較
    • 前者のほうが大きかったら、キーボードに隠れないということなので処理終了
    • 後者が大きければキーボードから隠れないように、両者の差分だけスクロール処理
  • updateScrollViewSize(_:duration:)でスクロール処理を実行
    • scrollViewcontentInset, scrollIndicatorInsetsを調整し、先ほど計算した差分の大きさだけ下方向に拡大
    • scrollViewcontentOffsetを調整し、y方向に同様の大きさだけスクロール
    • これらの処理を、取得済のアニメーション時間で実行

これで、キーボードが表示されるときにUIScrollViewが上方向にスクロールするようになった。(※UITextFieldが隠れる場合のみ)

キーボードが隠れるときにはこれの逆の操作をrestoreScrollViewSize:で行い、scrollViewcontentInset, scrollIndicatorInsetsをデフォルトの値に戻す。

完成

以上で、キーボードが隠れなくなり、自分が何を入力してるのか分かるようになった:smile:

scroll-sample.gif

ysk_1031
Software Engineer at Atrae, Inc. iOS, Android, Webの開発など色々やっています. 最近はyentaというアプリを作ったりしてます
atrae
People Techカンパニーとして、転職サイトGreen, ビジネスマッチングアプリyenta, 組織改善プラットフォームwevoxなどのサービスを運営。全ての社員が誇りを持てる組織と事業の創造にこだわり、関わる人々がファンとして応援したくなるような魅力ある会社であり続けることを目指しています。
https://atrae.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした