【徒然iOS】気ままにUIKit43〜Scroll Viewを使って入力欄とキーボードが被らないようにする方法〜
概要
このマガジンは四十を過ぎたおっさんが、
を参考にStoryboardでiOSアプリを完全に趣味で楽しんでいるだけな記事を気ままに上げてます。
今回
をハイ、レッツゴ🕺
前準備
今回は前回の続きをそのままやるとか書いてるけど、思いっきし、前回の結果を削除して1から作り直すのと変わらないので、いつもどおり、
念の為、
バックアップ
新しいクラス
ビューコントローラの追加
イニシャルビューの変更
をいつも通りやってから本題へ💃
本題
とりあえず、サイト記事にあるイメージを、
と、ここでお断り
7年近く前のサイト記事どおりに手順どおり操作してみたんだけど、
結構、
説明が抜けていたり、
手順どおりやってもおかしいところが多かったので、
ここからは、ちょっと自分の操作を中心にやってく〜〜〜〜
⒈スクロールビューを配置して、その中にイメージを配置してAutoLayoutを追加
サイト記事のとおりだと、イメージが左に寄りすぎて、シミュレータで実行しても変なので〜〜〜〜
⒉編集しやすいように、ビューコントローラーをFreedomに変更
と、スクロールビューの位置が変わってないので、編集しづらい
ので、
⒊テキストフィールドとテキストビューをサイト記事どおり配置
ここは、各パーツを
AutoLayoutの制約の際に、スクロールビューからの距離
幅は指定しないと自動的に変わるのでWidthも今回は設定する
に気をつければ、以外は、サイト記事の設定項目で制約を追加するだけ
⒋⒊で組んだパーツをアウトレット接続
⒌テキストフィールドのキーボードはReturnキーを押すと閉じられるようにAction接続を追加
⒍以下のコードを組み込む
//サイト記事のコード
import UIKit
class ViewController: UIViewController,UITextViewDelegate {
@IBOutlet weak var testTextView2: UITextView!
@IBOutlet weak var testTextView1: UITextView!
@IBOutlet weak var testTextField: UITextField!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//ビューを作成する。
let testView = UIView()
testView.frame.size.height = 60
testView.backgroundColor = UIColor.blueColor()
//「閉じるボタン」を作成する。
let closeButton = UIButton(frame:CGRectMake(CGFloat( UIScreen.mainScreen().bounds.size.width)-70, 0, 70, 50))
closeButton.setTitle("閉じる", forState:UIControlState.Normal)
closeButton.backgroundColor = UIColor.redColor()
closeButton.addTarget(self,action:"onClickCloseButton:", forControlEvents: .TouchUpInside)
//ビューに「閉じるボタン」を追加する。
testView.addSubview(closeButton)
//キーボードのアクセサリにビューを設定する。
testTextView1.inputAccessoryView = testView
testTextView2.inputAccessoryView = testView
}
//テキストフィールドの編集終了時の呼び出しメソッド
@IBAction func endInputTextField(sender: UITextField) {
}
//「閉じるボタン」で呼び出されるメソッド
func onClickCloseButton(sender: UIButton) {
//キーボードを閉じる
testTextView1.resignFirstResponder()
testTextView2.resignFirstResponder()
}
}
ここは様々なエラーが起きるので、
改修し終わったコードを載せるね〜〜〜〜
class ScrollKeyboardViewController: UIViewController,UITextViewDelegate {
@IBOutlet weak var myTextField: UITextField!
@IBOutlet weak var myText1View: UITextView!
@IBOutlet weak var myText2View: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
//ビューを作成する。
let myView = UIView()
myView.frame.size.height = 60
myView.backgroundColor = UIColor.blue
//「閉じるボタン」を作成する。
let closeButton = UIButton(frame:CGRectMake(CGFloat( UIScreen.main.bounds.size.width)-70, 0, 70, 50))
closeButton.setTitle("閉じる", for:UIControl.State.normal)
closeButton.backgroundColor = UIColor.red
closeButton.addTarget(self,action:#selector(TextViewStandardController.onClickCloseButton(_:)), for: .touchUpInside)
//ビューに「閉じるボタン」を追加する。
myView.addSubview(closeButton)
//キーボードのアクセサリにビューを設定する。
myText1View.inputAccessoryView = myView
myText2View.inputAccessoryView = myView
}
//テキストフィールドの編集終了時の呼び出しメソッド
@IBAction func endInputTextField(_ sender: UITextField) {
}
//「閉じるボタン」で呼び出されるメソッド
@objc func onClickCloseButton(_ sender: UIButton) {
//キーボードを閉じる
myText1View.resignFirstResponder()
myText2View.resignFirstResponder()
}
}
て感じで、閉じるボタンでキーボード閉じるまでは実装できたね〜〜〜🕺
⒎スクロールビューの移動を実装する
と、ここまでだとキーボードがスクロールビューに被っちゃうので、直す。
けども、、、
解説がやたら長いので〜〜〜〜
サイト記事の最後にある、コードで一気に直す🕺
import UIKit
class ViewController: UIViewController,UITextViewDelegate,UITextFieldDelegate {
@IBOutlet weak var testTextView2: UITextView!
@IBOutlet weak var testTextView1: UITextView!
@IBOutlet weak var testTextField: UITextField!
@IBOutlet weak var testScrollView: UIScrollView!
var target:UIView! //タップされた部品
var moveY:CGFloat = 0 //移動距離
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//ビューを作成する。
let testView = UIView()
testView.frame.size.height = 60
testView.backgroundColor = UIColor.blueColor()
//「閉じるボタン」を作成する。
let closeButton = UIButton(frame:CGRectMake(CGFloat( UIScreen.mainScreen().bounds.size.width)-70, 0, 70, 50))
closeButton.setTitle("閉じる", forState:UIControlState.Normal)
closeButton.backgroundColor = UIColor.redColor()
closeButton.addTarget(self,action:"onClickCloseButton:", forControlEvents: .TouchUpInside)
//ビューに「閉じるボタン」を追加する。
testView.addSubview(closeButton)
//キーボードのアクセサリにビューを設定する。
testTextView1.inputAccessoryView = testView
testTextView2.inputAccessoryView = testView
//キーボードが現れるときに通知するメソッドを登録する。
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeShown:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
//部品のデリゲート先に自分を設定する。
testTextField.delegate = self
testTextView1.delegate = self
testTextView2.delegate = self
}
//テキストフィールド編集前の呼び出しメソッド
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
target = textField
return true
}
//テキストビュー編集前の呼び出しメソッド
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
target = textView
return true
}
//キーボードが開くときの呼び出しメソッド
func keyboardWillBeShown(notification:NSNotification) {
//キーボードのフレームを取得する。
if let keyboardFrame = notification.userInfo![UIKeyboardFrameEndUserInfoKey]?.CGRectValue {
//部品とキーボードがかぶっていかを判定
let zureY = CGRectGetMaxY(target.frame) - testScrollView.contentOffset.y - CGRectGetMinY(keyboardFrame)
if (zureY > 0) {
//スクロールビューをずらす
testScrollView.contentInset.bottom += zureY
testScrollView.contentOffset.y += zureY
moveY += zureY
}
}
}
//テキストフィールドの編集終了時の呼び出しメソッド
@IBAction func endInputTextField(sender: UITextField) {
}
//「閉じるボタン」で呼び出されるメソッド
func onClickCloseButton(sender: UIButton) {
//キーボードを閉じる
testTextView1.resignFirstResponder()
testTextView2.resignFirstResponder()
}
//キーボードが閉じられるときの呼び出しメソッド
func keyboardWillBeHidden(notification:NSNotification){
//スクロールビューの位置を元に戻す。
UIView.animateWithDuration(0.5, animations: {self.testScrollView.contentOffset.y -= self.moveY})
testScrollView.contentInset = UIEdgeInsetsZero
moveY = 0
}
}
と思ったけど、コードをみると、
ScrollViewのアウトレット接続が思いっきり端折られているので、
不足分のコードをゆっくり追加していく〜〜〜〜
コードを追加して、オブジェクト名だけ直しても
今回のコード(まとめ)
class ScrollKeyboardViewController: UIViewController,UITextViewDelegate,UITextFieldDelegate{
@IBOutlet weak var myTextField: UITextField!
@IBOutlet weak var myText1View: UITextView!
@IBOutlet weak var myText2View: UITextView!
@IBOutlet weak var myScrollView: UIScrollView!
//タップされた部品
var target:UIView!
//移動距離
var moveY:CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
//ビューを作成する。
let myView = UIView()
myView.frame.size.height = 60
myView.backgroundColor = UIColor.blue
//「閉じるボタン」を作成する。
let closeButton = UIButton(frame:CGRectMake(CGFloat(UIScreen.main.bounds.size.width)-70, 0, 70, 50))
closeButton.setTitle("閉じる", for:UIControl.State.normal)
closeButton.backgroundColor = UIColor.red
closeButton.addTarget(self,action:#selector(TextViewStandardController.onClickCloseButton(_:)), for: .touchUpInside)
//ビューに「閉じるボタン」を追加する。
myView.addSubview(closeButton)
//キーボードのアクセサリにビューを設定する。
myText1View.inputAccessoryView = myView
myText2View.inputAccessoryView = myView
//キーボードが現れるときに通知するメソッドを登録する。
NotificationCenter.default.addObserver(self, selector: #selector(TextViewStandardLotateRightController.keyboardWillBeShown(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillBeHidden(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
//部品のデリゲート先に自分を設定する。
myTextField.delegate = self
myText1View.delegate = self
myText2View.delegate = self
}
//テキストフィールド編集前の呼び出しメソッド
@objc func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
target = textField
return true
}
//テキストビュー編集前の呼び出しメソッド
@objc func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
target = textView
return true
}
//キーボードが開くときの呼び出しメソッド
@objc func keyboardWillBeShown(_ notification:NSNotification) {
//キーボードのフレームを取得する。
if let keyboardFrame = notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
//部品とキーボードがかぶっていかを判定
let zureY = CGRectGetMaxY(target.frame) - myScrollView.contentOffset.y - CGRectGetMinY(keyboardFrame)
if (zureY > 0) {
//スクロールビューをずらす
myScrollView.contentInset.bottom += zureY
myScrollView.contentOffset.y += zureY
moveY += zureY
}
}
}
//テキストフィールドの編集終了時の呼び出しメソッド
@IBAction func endInputTextField(_ sender: UITextField) {
}
//「閉じるボタン」で呼び出されるメソッド
@objc func onClickCloseButton(_ sender: UIButton) {
//キーボードを閉じる
myText1View.resignFirstResponder()
myText2View.resignFirstResponder()
}
//キーボードが閉じられるときの呼び出しメソッド
@objc func keyboardWillBeHidden(_ notification:NSNotification){
//スクロールビューの位置を元に戻す。
UIView.animate(withDuration: 0.5, animations: {self.myScrollView.contentOffset.y -= self.moveY})
myScrollView.contentInset = UIEdgeInsets.zero
moveY = 0
}
}
ブラッシュアップ
今回も、ここまででやっちゃってるので、今回も割愛💦
感想
⒈サイト記事の説明が端折られすぎていて、
コード組み込みに行き着くまででもとにかく長い💦(良い感じにレイアウトするのに3時間くらい試行錯誤したわ🙇)
⒉コードにいざ行き着いても、今回のコードは長い上に、
すでにサンプルコードの書き振りなんかも大幅に変わっちゃってるので、
この記事だけを見て組み込もうって思う人がいても、
@Objcと#selector
各プロパティ値の書き方が変わってる
なんかの前提知識がないと、
正直、実行エラーばかりになって心が折れるかなと。
メリトクラシーな人は
「そんなことは説明されなくてもできて当たり前」
と思うかもしれないけど、初学者は、
知らない、わからないが当たり前だからね〜〜〜〜
それを端折って、自分で調べるような記事を書いても、時間がかかるばかりで、
「学習コストがかかるばかりで、面倒だから。
もうSwiftでのアプリ開発いいや。」
ってなるかなと、、、💦
自分のわかる/できることは
「説明してなくても、出来て当たり前」
って思ってる人が、最近多いけど、そういう人ほどうちらの業界だと
指示漏れ、伝達漏れ、連携不足、確認不足
👉思い込みと勘違い
👉バグ発生
にしかならないからね🤔
一度も過去に説明してないのに、「わかって当たり前」
はなしにしよう🕺
やってて思った本音は、
「これだからUIKitでの開発は嫌いなんだよ!。こんな簡単な組み込みSwiftUIなら五分くらいで出来るのに、、、
いまだに、UIKitとかでやってる現場もあるらしいけど可哀想に💦」
てことくらい藁🤣
こういうややこしい組み込みを数行ずつのコード書けばできるように
最新のフレームワークをAppleが4年近く前にオープンソース化してくれてんのに、日本語の市販本ではSwiftUIが出た当時で止まってるから、いまだにUIKitでやり続けようとする企業が多いと聞く👀
Apple公式
さて、次回は
をレッツゴする。
ScrollViewも最後だね〜〜〜
次回は設定項目だから、また一覧表を使って終わらすかな💦