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

[Swift] UITextFieldがキーボードに隠れないようにするやり方

More than 3 years have passed since last update.

[2017.02.09 Swift3.0に対応]
・ラベルの追加参考サイト
・println を printに変更
などなど


Xcodeのstoryboardを使用しないやり方です。
storyboardを使用して開発する方は、参考サイトが役立ちます。

はじめに

アプリ内でUITextFieldをタップすると下からニョキッとキーボードが出て来ます。
この時、UITextFieldがキーボードに隠れてしまい、
自分が何を入力しているのかが確認できなくなってしまうため、その解決方法を載せます。

ソースコードだけ見たい方は、一番下に完成コードを載せているので、そちらをどうぞ。

解決方法

UIScrollViewを使用します。
TextFieldの下辺とキーボードの上辺が重なったら、ScrollViewで指定した長さずらしてあげます。
そうすれば、TextFeildはキーボードに隠れません。

実装

それでは、以下の順に実装していきましょう。

1. プロジェクトの作成

今回はUITextFieldとUIScrollViewしか使用しないため、
Single View Applicationを選択してプロジェクトを作成します。

2. UITextFeild

2-1. UITextfieldの追加

text1text2の2つのUITextFieldを追加する。

ViewController.swift
class ViewController: UIViewController {
    let text1: UITextField = UITextField(frame: CGRect(x: 0,y: 0,width: 200,height: 30))
    let text2: UITextField = UITextField(frame: CGRect(x: 0,y: 0,width: 200,height: 30))

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        // 表示する文字を代入する.
        text1.text = ""
        text2.text = ""

        // 枠を表示する.
        text1.borderStyle = UITextBorderStyle.RoundedRect
        text2.borderStyle = UITextBorderStyle.RoundedRect

        // UITextFieldの表示する位置を設定する.
        text1.layer.position = CGPoint(x:self.view.bounds.width/2,y:500);
        text2.layer.position = CGPoint(x:self.view.bounds.width/2,y:100);

        self.view.addSubview(text1)
        self.view.addSubview(text2)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

これで実行すると下記のようになる。
text1が一番下でtext2が一番上のTextFieldです。
実行結果1.png
もし、text1とtext2のTextFieldを押してもキーボードが出てこない場合、Xcodeの設定を確認してみましょう。参考サイト
これだけだと、
TextFieldのキーボードのEnterを押してもキーボードが閉じません

2-2. Enterを押したらキーボードが閉じるようにする

下記の関数をViewController.swift内に追加する

    //改行ボタンが押された際に呼ばれる.
    func textFieldShouldReturn(textField: UITextField) -> Bool {
        textField.resignFirstResponder()

        return true
    }

これで、TextFieldのキーボードのEnterを押したらキーボードが閉じるようになりました。

3. UIScrollView

このままだとtext1がキーボードに隠れているため、UIScrollViewで隠れないようにします。

3-1 UIScrollViewの追加

UIScrollViewを使用するために必要な処理を書いていきます。
以下が追加された後のViewController.swiftです。

ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate, UIScrollViewDelegate{
    let text1: UITextField = UITextField(frame: CGRectMake(0,0,200,30))
    let text2: UITextField = UITextField(frame: CGRectMake(0,0,200,30))
    let sc = UIScrollView();

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        sc.frame = self.view.frame;
        sc.backgroundColor = UIColor.red;
        sc.delegate = self;

        //textfileの位置を指定する
        sc.contentSize = CGSize(width: 250,height: 1000)
        self.view.addSubview(sc);

        // 表示する文字を代入する.
        text1.text = ""
        text2.text = ""

        // Delegateを設定する.
        text1.delegate = self
        text2.delegate = self

        // 枠を表示する.
        text1.borderStyle = UITextBorderStyle.roundedRect
        text2.borderStyle = UITextBorderStyle.roundedRect

        // UITextFieldの表示する位置を設定する.
        text1.layer.position = CGPoint(x:self.view.bounds.width/2,y:500);
        text2.layer.position = CGPoint(x:self.view.bounds.width/2,y:100);

        self.view.addSubview(text1)
        self.view.addSubview(text2)

        // Viewに追加する
        sc.addSubview(text1)
        sc.addSubview(text2)

    }

    //改行ボタンが押された際に呼ばれる.
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()

        return true
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

UIScrollViewの範囲を確認できるように、
sc.backgroundColor = UIColor.redColor();
を追加したため、背景が赤色になっています。

4. TextFieldを上にスクロールさせる処理

ここから、TextFieldとキーボードが重なったときにスクロールさせる処理を書いていきます。

4.1 UITextFieldの情報を格納する

UITextFieldの情報を格納するための変数を定義します。

var txtActiveField = UITextField()

text1またはtext2のUITextFieldが押されたとき、
その情報をtxtActiveFieldへ格納するための関数を以下に示します。

//UITextFieldが編集された直後に呼ばれる.
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {

txtActiveField = textField
   return true
}

4.2 UITextFieldの下辺とキーボードの上辺が重なっているかどうか調べるための処理

override func viewWillAppear(_ animated: Bool) {

    super.viewWillAppear(animated)

    let notificationCenter = NotificationCenter.default
    notificationCenter.addObserver(self, selector: #selector(ViewController.handleKeyboardWillShowNotification(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    notificationCenter.addObserver(self, selector: #selector(ViewController.handleKeyboardWillHideNotification(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

}

func handleKeyboardWillShowNotification(_ notification: Notification) {


    let userInfo = notification.userInfo!
    let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let myBoundSize: CGSize = UIScreen.main.bounds.size

    var txtLimit = txtActiveField.frame.origin.y + txtActiveField.frame.height + 8.0
    let kbdLimit = myBoundSize.height - keyboardScreenEndFrame.size.height


     print("テキストフィールドの下辺:(\(txtLimit))")
     print("キーボードの上辺:(\(kbdLimit))")


    if txtLimit >= kbdLimit {
        sc.contentOffset.y = txtLimit - kbdLimit
    }
}


func handleKeyboardWillHideNotification(_ notification: Notification) {


    sc.contentOffset.y = 0
}

4.3 スクロールする領域を知りたい場合は下記の関数を書けばLogで確認することが出来ます。

func scrollViewDidScroll(scrollView: UIScrollView)
{
        // scroll領域取得
        NSLog("%f %f", scrollView.contentOffset.x,scrollView.contentOffset.y);
}

完成版ソースコード

完成したプログラムを以下に示します。
新しく作ったプロジェクトのViewController.swiftファイルを
以下のコードに書き換えるだけで、
UITextFieldがキーボードに隠れないようにするやり方を確認することが出来ます。

ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate, UIScrollViewDelegate{
    let text1: UITextField = UITextField(frame: CGRect(x: 0,y: 0,width: 200,height: 30))
    let text2: UITextField = UITextField(frame: CGRect(x: 0,y: 0,width: 200,height: 30))
    let sc = UIScrollView();
    var txtActiveField = UITextField()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        sc.frame = self.view.frame;
        sc.backgroundColor = UIColor.red;
        sc.delegate = self;

        //textfileの位置を指定する
        sc.contentSize = CGSize(width: 250,height: 1000)
        self.view.addSubview(sc);

        // 表示する文字を代入する.
        text1.text = ""
        text2.text = ""

        // Delegateを設定する.
        text1.delegate = self
        text2.delegate = self

        // 枠を表示する.
        text1.borderStyle = UITextBorderStyle.roundedRect
        text2.borderStyle = UITextBorderStyle.roundedRect

        // UITextFieldの表示する位置を設定する.
        text1.layer.position = CGPoint(x:self.view.bounds.width/2,y:500);
        text2.layer.position = CGPoint(x:self.view.bounds.width/2,y:100);

        self.view.addSubview(text1)
        self.view.addSubview(text2)

        // Viewに追加する
        sc.addSubview(text1)
        sc.addSubview(text2)

    }

    //改行ボタンが押された際に呼ばれる.
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()

        return true
    }

    //UITextFieldが編集された直後に呼ばれる.
    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        txtActiveField = textField
        return true
    }

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

        let notificationCenter = NotificationCenter.default
        notificationCenter.addObserver(self, selector: #selector(ViewController.handleKeyboardWillShowNotification(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        notificationCenter.addObserver(self, selector: #selector(ViewController.handleKeyboardWillHideNotification(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    func handleKeyboardWillShowNotification(_ notification: Notification) {

        let userInfo = notification.userInfo!
        let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let myBoundSize: CGSize = UIScreen.main.bounds.size
        var txtLimit = txtActiveField.frame.origin.y + txtActiveField.frame.height + 8.0
        let kbdLimit = myBoundSize.height - keyboardScreenEndFrame.size.height

        print("テキストフィールドの下辺:(\(txtLimit))")
        print("キーボードの上辺:(\(kbdLimit))")

        if txtLimit >= kbdLimit {
            sc.contentOffset.y = txtLimit - kbdLimit
        }
    }

    func handleKeyboardWillHideNotification(_ notification: Notification) {
        sc.contentOffset.y = 0
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

ダウンロードしてそのまま実行させて確認したいって方は下からどうぞ。
https://github.com/chankei1/scroll_sample

参考サイト

「Swiftでテキストフィールドがキーボードに隠れないようにしてみた」
URL:http://ameblo.jp/hayashidesuga/entry-11971210696.html
「Xcode 6のiOS Simulatorでキーボードが表示されない場合の対処法」
URL:http://yutaihara.com/archives/147

kobaboy
自己満足ですが誰かの役に立てたら幸いです 少年のような心を忘れずに
fignny
「リアル」と「バーチャル」の境界を意識しない世界をつくります
https://fignny.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