前提・実現したいこと
画面の下の方に設置しているtextFieldが、キーボード表示時に隠れてしまいます。
上記問題を解決するため下記のサイトを参考にしたのですが、1つの画面では上手くいき、もう一つの画面では上手くいきません。
https://ayafitpay.com/hidden-ios-keyboard/
上手く行かなかった画面は、OverFullScreenを使ってポップアップのように表示しているという違いがあり、それが原因だとは思うのですが、では実際にどうすれば良いのかが分からず苦戦しています。
真ん中の画面が上手くいった画面です。(一番下のtextFieldがキーボードに合わせてスクロールされる)
一番右の画面が上手くいかない画面です。(textFieldをタップしてもキーボードに合わせてスクロールしない。なぜか後ろに透けて見える前の画面がスクロールされている様子)
追記:解決策はまだ分かっていませんが、原因は分かったため、質問の一番下に「原因」の見出しで情報を追加しています。
該当のソースコード
textFieldや本件に関係ないコードも多いので、見やすいように一部省略しています。
Swift
1import UIKit 2 3class testController: UIViewController { 4 5 var selectedTextField: UITextField? 6 7 @IBOutlet weak var scrollView: UIScrollView! 8 @IBOutlet weak var testField1: UITextField! 9 @IBOutlet weak var testField2: UITextField! 10 @IBOutlet weak var testField3: UITextField! 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 15 self.textFieldInit() 16 17 } 18 19 override func viewWillAppear(_ animated: Bool) { 20 super.viewWillAppear(animated) 21 // キーボードイベントの監視開始 22 NotificationCenter.default.addObserver(self,selector: #selector(keyboardWillBeShown(notification:)),name: UIResponder.keyboardWillShowNotification,object: nil) 23 24 NotificationCenter.default.addObserver(self,selector: #selector(keyboardWillBeHidden(notification:)),name: UIResponder.keyboardWillHideNotification,object: nil) 25 } 26 27 override func viewWillDisappear(_ animated: Bool) { 28 super.viewWillDisappear(animated) 29 // キーボードイベントの監視解除 30 NotificationCenter.default.removeObserver(self,name: UIResponder.keyboardWillShowNotification,object: nil) 31 32 NotificationCenter.default.removeObserver(self,name: UIResponder.keyboardWillHideNotification,object: nil) 33 } 34 35 36 /* 37 // MARK: - Navigation 38 39 // In a storyboard-based application, you will often want to do a little preparation before navigation 40 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 41 // Get the new view controller using segue.destination. 42 // Pass the selected object to the new view controller. 43 } 44 */ 45 46} 47 48extension testController: UITextFieldDelegate { 49 50 func textFieldInit() { 51 // 最初に選択されているTextFieldをセット 52 self.selectedTextField = self.testField1 53 54 // 各TextFieldのdelegate 色んなイベントが飛んでくるようになる 55 self.testField1.delegate = self 56 self.testField2.delegate = self 57 self.testField3.delegate = self 58 59 } 60 61 // キーボードが表示された時に呼ばれる 62 @objc func keyboardWillBeShown(notification: NSNotification) { 63 if let userInfo = notification.userInfo { 64 if let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue, let animationDuration = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue { 65 restoreScrollViewSize() 66 67 let convertedKeyboardFrame = scrollView.convert(keyboardFrame, from: nil) 68 //現在選択中のTextFieldの下部Y座標とキーボードの高さから、スクロール量を決定 69 let offsetY: CGFloat = self.selectedTextField!.frame.maxY - convertedKeyboardFrame.minY 70 if offsetY < 0 { return } 71 updateScrollViewSize(moveSize: offsetY, duration: animationDuration) 72 } 73 } 74 } 75 76 // キーボードが閉じられた時に呼ばれる 77 @objc func keyboardWillBeHidden(notification: NSNotification) { 78 restoreScrollViewSize() 79 } 80 81 // TextFieldが選択された時 82 func textFieldDidBeginEditing(_ textField: UITextField) { 83 // 選択されているTextFieldを更新 84 self.selectedTextField = textField 85 } 86 87 // リターンが押された時 88 func textFieldShouldReturn(_ textField: UITextField) -> Bool { 89 // キーボードを閉じる 90 textField.resignFirstResponder() 91 return true 92 } 93 94 // moveSize分Y方向にスクロールさせる 95 func updateScrollViewSize(moveSize: CGFloat, duration: TimeInterval) { 96 UIView.animate(withDuration: duration, 97 delay: 0.025, 98 options: .curveLinear, 99 animations: { 100 let contentInsets = UIEdgeInsets(top: 0, 101 left: 0, 102 bottom: moveSize, 103 right: 0) 104 self.scrollView.contentInset = contentInsets 105 self.scrollView.scrollIndicatorInsets = contentInsets 106 self.scrollView.contentOffset = CGPoint(x: 0, 107 y: moveSize) 108 }, 109 completion: nil) 110 } 111 112 func restoreScrollViewSize() { 113 // キーボードが閉じられた時に、スクロールした分を戻す 114 self.scrollView.contentInset = UIEdgeInsets.zero 115 self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero 116 } 117} 118 119
追記
現在のストーリーボード上のViewの階層と、そのほか関係の有りそうな項目を追記いたします
最初のScrollViewが参考サイトのとおりに作った所です。
SafeAreaいっぱいに広げ、すぐ下のViewはSafeAreaと同じ縦横幅に設定しています。
上記Viewは透過させ、その上に(階層としては下?に)Viewを余白4方向20で設置し、ポップアップのように見せています。
ポップアップ用のViewに見出し用ラベル、textFieldを設置するScrollView、ボタン設置用のStackViewを置いています。
UIViewControllerにOutletさせているのは最初のScrollViewです。
スクロールビューが2つあることが駄目なのでしょうか。
その他気になる所
・Extentionの使い方がよく分からず、前の画面にも同じExtentionを記載しています。
※クラス名やOutletさせるtextFieldは変えています
原因
textFieldをStackViewの中に入れているなど、入れ子が複雑な(深い?)状態になっていました。
そして、勉強中なので間違っているかもしれませんが、self.selectedTextField!.frame.maxYは親Viewからの位置を取得するようですので、スクロールビューからの距離ではなく、StackViewからの距離を取得しているみたいです。
結果、0 − キーボードサイズを計算し、offsetY < 0 { return }によって何も起きないという状況と思われます。
じゃあどうすればスクロールビューからtextFieldまでの距離が取れるのかなどまだわからない点も多いので自己解決には記載できないのですが、取り急ぎご報告です。
回答1件
あなたの回答
tips
プレビュー