今回はアプリでちょっとしたサービスを作るときに使えるテクニックをご紹介します。たとえばログイン画面みたいに文字の入力が必要になるとき、テキストフィールドをタップするとキーボードが下からニョキッと出て来ます。しかしキーボードが結構大きいので入力しようと思っていた部分を覆い隠してしまうことがあって、何を入力しているのか見えなくなって困ってしまいます。今回はそれを回避するための方法を説明します。
ここではストーリーボードを使用した説明になりますが、使用しない場合のやり方は以下の方が書いた記事をご参照ください。
[Swift] UITextFieldがキーボードに隠れないようにするやり方(kobaboyさん)
あと、Swift2.0対応しました。
学べること
- スクロールビューの使い方
- 不要なキーボードを隠す方法
始めましょう!
プロジェクトの新規作成
まずはプロジェクトを新規作成します。名前は「LoginSample」としておきましょう。テンプレートは「Single View Application」を選択してください。Core Dataのチェックも不要です。手順の詳細は以下をご参照ください。
オブジェクトの配置
それでは必要なオブジェクトを貼り付けていきます。今回は3層に重ねて配置しますので、順番に気をつけてくださいね。構造はスクロールビューがあって、その中に普通のビューを入れて、そのビューの上にテキストフィールドが2つ入るようになります。
まずはスクロールビューから行きましょう。
スクロールビューを配置する
スクロールビューをストーリーボード上にドラッグして貼り付けます。そうしたら先にサイズの調整をしておきましょう。Size Inspectorで画面いっぱいのサイズ、X=0、Y=0、Width=600、Height=600にしておきます。
今度はスクロールビューにレイアウトの制約をつけておきます。これは、iPhone4sやiPhone6plusのように画面の大きさが機種によって違うので、異なるサイズでも自動的に調整してくれるための便利な機能です。
画面下の方にあるPinというアイコンをクリックしてください。開いたウィンドウの上部に、上下左右の余白を設定する4つの入力項目と、その中央の小さな四角があります。
その少し下のConstrain to marginsからチェックを外して(説明しやすくするためです。お好みでどうぞ)、上下左右の数字が0なのを確認しつつ、その側にある薄い赤線をクリックして濃い色にしましょう。終わったらAdd 4 Constraintsボタンを押せば制約の作成は完了です。この作業によって、スクリーンのサイズが異なっても、画面の大きさにピッタリ合わせてスクロールビューが配置されるようになりました。上下左右に設定した0というのは、画面枠との余白を0に設定するためです。
スクロールビューはプログラム側からも制御するので、今のうちにアウトレット接続しておきましょう。アウトレット名は「scvBackGround」にしておきます。スクロールビューがストーリーボード上から選択しにくい場合は、階層表示されている中からcontrolキーを押しながらドラッグすると楽に操作できます。
アウトレット接続作成の詳細は以下をご参照ください。
ビューを配置する
次にスクロールビューの中にもう一度通常のビューを配置します。配置後、Viewの中にScroll view、さらにその中にViewを入れて3つの階層になっていることを確認してください。ここまでできたら先にサイズの調整をしておきましょう。Size Inspectorで一番外側のビューと同じサイズ、X=0、Y=0、Width=600、Height=400にしておきます(なぜ400なのかは後ほど説明します)。
配置が終わったら制約を付けます。画面右下にあるPinアイコンをクリックして、スクロールビューのときと同じように上下左右の余白を0にします。加えて、Heightの項目をチェックしましょう。あとはAdd 5 Constraintsをクリックします。この時点で警告が表示されますが、慌てずに次の手順に進みましょう。
最後に、今作成したビューをContorlキーを押しながらドラッグして、Scroll Viewの外側にあるもう一つのビューまでドラッグします。すると黒いウィンドウが表示されるので、Equal Widthsを選択してください。これはスクロールビューの幅を常に画面の幅(一番外側のビュー)と一緒にするよ、という意味があります。これによって横にスクロールする余地をなくして、縦スクロールだけするようになります。縦や横にスクロールするゲームを作るときにも役たちそうですね。
テキストフィールドを配置する
レイアウトはこれで最後です。あとはテキストフィールドを2つ、ビューの上に配置します。1つめはX=8、Y=50、Width=97、Height=30にして、2つめはX=8、Y=300、Width=97、Height=30にしておきます。ここでは特に制約をつけなくても大丈夫です。
あとはテキストフィールドに文字を入力するタイミングをキャッチできるようデリゲートを設定してレイアウト作成は終了です。ストーリーボードの階層表示画面でテキストフィールドをcontrolキーを押しながらドラッグして、黄色いアイコンのView Controllerまでドラッグしていきます。表示される黒いウィンドウの中にあるdelegateを選択すれば完了です。両方のテキストフィールドに対して同じ作業を行います。よくこれを忘れて、あれ?動かないぞ?と悩むことが多いんですよね。
ここまできたらあとはコーディング
ここからプログラムを書く作業になります。まず最初に、今の1文をviewDidLoadの上に挿入してください。これは、2つあるテキストフィールドのどちらが選択されたのかを判定するために利用します。
var txtActiveField = UITextField()
次はファンクションを追加します。textFieldShouldBeginEditingはテキストフィールドがタップされて入力状態になったときに呼ばれます。ここでタップされたテキストフィールドの情報を取得して、上記で作成した変数にセットしています。textFieldShouldReturnはリターンキーが押されたときに呼ばれて、下ではview.endEditingという、キーボードを閉じる処理を追加しています。キーボードは出てくるときは自動なのに閉じるときはこうやって書いてあげないとなりません。
func textFieldShouldBeginEditing(textField: UITextField!) -> Bool { txtActiveField = textField return true } func textFieldShouldReturn(textField: UITextField!) -> Bool { view.endEditing(true) return true }
今度はキーボード出てきたり引っ込んだりした時の処理を作ります。以下の2つのファンクションを用意します。上のファンクションはキーボードが表示された時の処理で、ここではフォーカスされたテキストフィールドがキーボードと被ってないかチェックして、被ってたらスクロールビューを上にずらします。途中で8をプラスしているのは、テキストフィールドとキーボードの間に少し余白を付けたかったためです。下の方はキーボードが閉じたときにずらした分を戻しています。
func handleKeyboardWillShowNotification(notification: NSNotification) { let userInfo = notification.userInfo! let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue() let myBoundSize: CGSize = UIScreen.mainScreen().bounds.size let txtLimit = txtActiveField.frame.origin.y + txtActiveField.frame.height + 8.0 let kbdLimit = myBoundSize.height - keyboardScreenEndFrame.size.height print("テキストフィールドの下辺:\(txtLimit)") print("キーボードの上辺:\(kbdLimit)") if txtLimit >= kbdLimit { scvBackGround.contentOffset.y = txtLimit - kbdLimit } } func handleKeyboardWillHideNotification(notification: NSNotification) { scvBackGround.contentOffset.y = 0 }
これで最後の工程になります。実は今作ったキーボード動作時のファンクションはこのままだと動きません。ですので動くように指定してあげる必要がありますので、viewWillAppearとviewDidDisappearにチョコチョコっと足してあげます。上の ファンクションでキーボードが出し入れする時の処理を登録、下の処理で解放しています。
override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) let notificationCenter = NSNotificationCenter.defaultCenter() notificationCenter.addObserver(self, selector: "handleKeyboardWillShowNotification:", name: UIKeyboardWillShowNotification, object: nil) notificationCenter.addObserver(self, selector: "handleKeyboardWillHideNotification:", name: UIKeyboardWillHideNotification, object: nil) } override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) let notificationCenter = NSNotificationCenter.defaultCenter() notificationCenter.removeObserver(self, name: UIKeyboardWillShowNotification, object: nil) notificationCenter.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil) }
実行します!
エミュレータをiPhone4sにすると、画面が小さくなってスクロールが実感しやすくなるとおもいます。これで実行すると、テキストフィールドが2つあって、上を選択した場合はスクロールしませんが、下の方を選択した場合にはキーボードに隠れない位置までスイーっとスクロールしてくれます。リターンキーを押してキーボードを閉じれば元に位置に戻ります。
途中で内側のビューの高さを400にしていたのは、600にしておくとiPhone4sの画面サイズよりも高くなり、起動した時点で上下スクロールできるようになってありがたみが薄れるからでした。気になる方は600にして実行してみても面白いですよ。
テキストフィールドに文字が入力できる状態になってもシミュレーターではキーボードが表示されない場合があります。その時はシミュレーターのメニューから「Hardware」-「Keyboard」-「Toggle Software Keyboard」を選択してみてください。
最後に
スクロールビューは理解するまで非常にわかりにくいので、サンプルを見たりしていろいろ動かしてみると良いですよ。
今回のサンプルはこちらです。
ストーリーボードでの画面作成は作るのは簡単ですが説明が面倒ですね。もう少しわかりやすいようにこっそり改善していきたいと思います。
コメント