【Swift/Xcode】TextFieldの座標を取得してキーボードを表示

やりたいこと

飛び出てくるキーボードに合わせて画面をスクロールするってやつです。座標の取得が難しくてこれが精一杯でした。

ちなみにこの動画のアプリは実際にAppleStoreの審査をかいくぐり、リリースまでこじつけました。

ご興味がある方は、ぜひダウンロードしてみて下さい!

言葉をストックするメモ帳「KotobaPlus」

開発環境

  • Swift 5.0.1
  • Xcode11.3
  • Google-Mobile-Ads-SDK

参考にした記事&ドキュメント

[Swift 4.2] キーボード表示時に画面をずらして、TextField等が隠れないようにする[iOS 12] - Qiita
Swift 4.2 では少し違っていたため書きました。 ViewController.swift // MARK: - ライフサイクル override func viewWillAppear(_ animate...
Apple Developer Documentation

コードの解説

では、コードの解説です。

※編集するファイルはTextFieldを設置したファイルです

全体のコード

import UIKit
import RealmSwift

class SheetEditViewController: UIViewController, UITextFieldDelegate {
    
    
    @IBOutlet weak var editLabel: UITextField!
    
    @IBOutlet weak var editWord: UITextField!
    
    @IBOutlet weak var sheetEditLabel: UILabel!
    
    @IBOutlet weak var sheetEditWord: UILabel!
    
    //引き継ぎラベルIDが入ってくる
    var idlabel:String?
    //引き継ぎラベル名が入ってくる
    var namelabel:String?
    //引き継ぎセレクトワードが入ってくる
    var wordselect:String?
    var checkWord:String?
    
    //TextFieldは2つあるからスクロール移動用に一つにまとめる
    var selectedTextField: UITextField?
    //画面の高さを取得
    let screenSize = UIScreen.main.bounds.size

    override func viewDidLoad() {
        super.viewDidLoad()
        editLabel.delegate = self
        editWord.delegate = self

    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.configureObserver()
        lodeFile()
    }
    
    func lodeFile() {
        sheetEditLabel.text = namelabel
        sheetEditWord.text = wordselect
        editLabel.text = namelabel
        editWord.text = wordselect
    }
    
    func configureObserver() {
        let notification = NotificationCenter.default
        
        notification.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        
        notification.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
        
        print("Notificationを発行")
    }
    
    //選択したテキストフィールドを認識
    func textFieldDidBeginEditing(_ textField: UITextField) {
        self.selectedTextField = textField
    }
    
    @objc func keyboardWillShow(_ notification: Notification?) {
        
            guard let rect = (notification?.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                let duration = notification?.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else {
                    return
            }
                
            //スクリーンの高さ
            let screenHeight = screenSize.height
            //キーボードの高さ
            let keyboardHeight = rect.size.height
            //選択したテキストフィールドまでの高さ
            let textUnderHeight: CGFloat = selectedTextField!.frame.maxY
            //スクロールする高さを計算
            let hiddenHeight = keyboardHeight + textUnderHeight - screenHeight
            
            if hiddenHeight > 0 {
                UIView.animate(withDuration: duration) {
                let transform = CGAffineTransform(translationX: 0, y: -(hiddenHeight + 20))
                self.view.transform = transform
                }
            } else {
                UIView.animate(withDuration: duration) {
                let transform = CGAffineTransform(translationX: 0, y: -(0))
                self.view.transform = transform
                }
            }
        print("keyboardWillShowを実行")
    }
    

    @objc 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を実行")
    }
    
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        //キーボードを閉じる処理
        view.endEditing(true)
    }

※コードは一部を抜粋しています

Notificationの発行

まず、大事なのが今回は「キーボード」というアプリ内で最初から決まっている情報を使うのでNotification(お知らせ)なるものを発行します。そうしないとキーボードの高さなどを取得できないからです。

    func configureObserver() {
        let notification = NotificationCenter.default
        
        notification.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        
        notification.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
        
        print("Notificationを発行")
    }

このコードで「Notificationの発行」を定義します。今回は「keyboardWillShow」というキーボードが呼び出された時と、「keyboardWillHide」というキーボードが閉じた時にお知らせしてくれるように定義しています。

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.configureObserver()
        lodeFile()
    }

このコードの

self.configureObserver()

この一行で「Notificationの発行」を呼び出します。

タイミングは「viewWillAppear」にしておきます。

keyboardWillShowの実行

    @objc func keyboardWillShow(_ notification: Notification?) {
        
            guard let rect = (notification?.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                let duration = notification?.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else {
                    return
            }
                
            //スクリーンの高さ
            let screenHeight = screenSize.height
            //キーボードの高さ
            let keyboardHeight = rect.size.height
            //選択したテキストフィールドまでの高さ
            let textUnderHeight: CGFloat = selectedTextField!.frame.maxY
            //スクロールする高さを計算
            let hiddenHeight = keyboardHeight + textUnderHeight - screenHeight
            
            if hiddenHeight > 0 {
                UIView.animate(withDuration: duration) {
                let transform = CGAffineTransform(translationX: 0, y: -(hiddenHeight + 20))
                self.view.transform = transform
                }
            } else {
                UIView.animate(withDuration: duration) {
                let transform = CGAffineTransform(translationX: 0, y: -(0))
                self.view.transform = transform
                }
            }
        print("keyboardWillShowを実行")
    }

「画面の高さ」と「キーボードの高さ」と「画面の上から選択したTextFieldまでの高さ」を取得して、スクロールする長さを出します。

ifで高さが0の時には動かさない条件分岐をしてみました。

「参考した記事&ドキュメント」に載せたApple公式ドキュメントのリンクを読んでおくと、XやらYがなんとなく理解できて面白いです!

この計算を変更すればスクロールする長さも自由に変更できます!⋯⋯が、座標の取得ってそう簡単に行かないのでちょっとしか遊べませんでした。

座標を自由自在に取得できれば、すごく楽しそうなのでもっと勉強していきたいところですね。

keyboardWillHideの実行

    @objc 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を実行")
    }

ここはそんな難しくないです。

以上で出現したキーボードに合わせて画面をスクロールするやつが実装できました!

最後に

管理人は現時点でプログラミン歴5ヶ月程度の未熟者ですm(_ _)m

アンラップのやり方とかまだまだ理解が浅くて、アプリクラッシュにヒヤヒヤしてます。

自分の備忘録やアウトプットを癖づけるためにブログをはじめましたが、ご指摘ありましたら勉強になりますので、ご遠慮なく言ってくれると嬉しいです。

ここまで読んでいただき、ありがとうございました!

コメント

タイトルとURLをコピーしました