「 SwiftUIでテキスト入力機能が欲しい… 」
「 アプリに文字入力ボックスを配置したい… 」
アプリケーション開発において、テキスト入力機能が必要な場面は多くありますよね。そんな時は「TextField(テキストフィールド)」を使いましょう。
今回は、SwiftUIでの開発でアプリ内のテキスト入力機能に必要不可欠である、TextFieldをご紹介します。
初学者の方でもわかりやすいよう、できるだけ噛み砕いた表現で、わかりやすく解説していきますので、安心して読み進めていただければと思います。
本記事の内容は、CodeCandyが公開しているYouTubeライブでも解説されています。記事の最後に紹介していますので、ぜひそちらもご覧になってください!
[ 本記事はこんな人におすすめ ]
・SwiftUIを学習したい
・SwiftUIで文字入力機能を使いたい
・データバインディングについて学びたい
まず初めに、TextFieldとはどういうものなのか見ていきます。
TextFieldとは、文字を入力して送信したり、チャットなどでよく使われるようなViewパーツのことを指します。
テキスト入力を受け付けて、入力データを取得できます。
私たちも普段からさまざまなアプリケーション内でこのTextFieldに文字を打ち込んでいますね。
(最もシンプルなTextFieldの記述例)
struct ContentView: View {
@State var inputName = "" // 状態変数としてTextFieldに紐付け
var body: some View {
TextField("名前を入力してください", text: $inputName) // ⬅︎
} // body
} // View
第一引数titleKeyは、プレースホルダとして表示されるテキストを設定します。
プレースホルダとは、TextFieldの入力ボックスが未入力の時にテキストボックスに表示される文字です。
下記画像に灰色の文字で「名前を入力してください」と書かれていますね。アプリ使用者に文字の入力を促すような記述がされているのが一般的です。
(引数titleKeyは記述時に省略することができます)
第二引数には文字列型の変数名を記述します。
もう一度使用例を見てみましょう。
TextField("名前を入力してください", text: $inputName)
引数として変数を指定する際に、頭文字に「$」を付与していますね。
これはプロパティ(変数inputName)への参照(Binding)を指定しています。
このように、引数「text」に状態変数のプロパティを紐づけることでTextFieldを使用できます。
「状態変数」「Binding(バインディング)」に関しては、後の項目で解説します。
SwiftUIが提供している機能「プロパティラッパー」の一種です。
プロパティラッパーとは、文字通りプロパティをラップする(包み込む)ことで、
プロパティに追加機能を付与することができます。
@State var inputName = "" // 状態変数
SwiftUIで定義する変数(今回の場合inputName)に対しては通常、値を更新することができません。
@Stateを変数宣言時に付与することで、変数の値を更新することができます。
@Stateが付与された変数のことは状態変数(じょうたいへんすう)とも呼ばれます。
SwiftUIは、「@State」が付与された変数を常に監視します。状態変数の値が更新されることで、SwiftUIがbody(画面)を再描画します。
引数として指定した状態変数に$をつけることで、その変数の「参照」を渡します。
参照を渡すことで、データの読み書きを双方向で行うことができます。
「参照」といきなり言われてもイメージが付き辛いかもしれませんが、要は、変数とTextFieldのデータを双方向に紐付けているというイメージです。
このことを「データ連動(バインド)」と呼びます。
データ連動(バインド)の挙動が分かる一例を見てみます。
コード7行目に、「Text(inputName)」を追加しました。
これにより、状態変数inputNameの値をテキストとして画面に出力します。
struct ContentView: View {
@State var inputName = "" // 状態変数としてTextFieldに紐付け
var body: some View {
VStack { // 要素を縦列に並べる
Text(inputName) // ⬅︎
.font(.largeTitle)
TextField("名前を入力してください", text: $inputName)
.padding()
} // VStack
} // body
} // View
では、シミュレータ [Command + R] を起動してみましょう。
Textの値は画面赤枠のあたりに配置されています。
今はまだ、状態変数「inputName」の値が空なので、何も表示されていませんね。
ではこの状態から、TextFieldに文字を入力するとどうなるでしょうか。
Textの値も連動して更新されていることがわかりますね!
TextFieldに変数「inputName」をバインディング(連動)していることで、TextFieldに値を入れると変数の値も連動して更新されます。
そして、変数に@Stateを付与していることで、変数の値の変化をSwiftUIが検知し、自動で画面の表示を更新してくれているという処理の流れです。
「textFieldStyle」はViewパーツに付与するモディファイア機能の一種です。
SwiftUIのtextFieldは、デフォルトではスタイルが設定されていません。
こちらのモディファイアを使用することで、入力ボックスに枠線を追加することができます。
スタイルを(.roundedBorder)とすることで、枠線の角が丸みを帯びます。
TextField("名前を入力してください", text: $inputName)
.textFieldStyle(.roundedBorder) // ⬅︎
.padding()
画像だと少しみづらいですが、TextFieldに薄く枠線が追加されています。
ユーザーがTextFieldの入力を終了した時(改行/returnが実行された時)に、クロージャ内の処理を実行することができます。
私たちが普段アプリ内でTextFieldに文字入力をした後、エンターキーを押すことで検索がスタートするという挙動も、このonSubmitの機能によって実装することができます。
コード例を見てみましょう。
TextFieldに「onSubmit」モディファイアが付与されており、
クロージャ内でprint出力が記述されています。
struct ContentView: View {
@State private var inputName = ""
var body: some View {
VStack {
TextField("名前を入力してください", text: $inputName)
.textFieldStyle(.roundedBorder)
.padding()
.onSubmit { // ⬅︎ クロージャ内の処理を実行
print("入力された値: \(inputName)")
} // onSubmit
} // VStack
} // body
} // View
TextFieldに文字を入力して、改行/retuen を押すと…
デバッグエリアに指定のテキストがprint出力されていることがわかりますね。
※注意
print出力は、シミュレータ [Command + R] 内でのみ実行されます。
プレビュー画面内では動作しません。
「onChange」とは、指定した監視対象の値が更新されるタイミングで、
変化した値を自動で受け取り、受け取った値をもとに何らかの処理を行うことができるイベント検知機能です。
TextField("名前を入力してください", text: $inputName)
.textFieldStyle(.roundedBorder)
.padding()
.onChange(of: inputName) { newValue in // ⬅︎
print("入力されている値: \(newValue)")
}
onSubmitと異なる点として、監視対象を指定するという違いがあります。
onChangeの引数「of:」に、監視対象を指定します。
コード例では、変数「inputName」を監視対象とし、変数の値が変更されるとonChangeがその値を受け取り、クロージャ内の処理を実行します。
受け取った値は、処理クロージャ最初の「{ ◯◯ in …」の◯◯に格納されます。
◯◯の命名は任意の名前指定が可能です。(例での命名はnewValue)
シミュレータ [Command + R] を起動して、onChangeの動きを確認してみましょう。
一入力文字が更新されるたびに、デバッグエリアにprint出力されていますね。
textFieldが変数inputNameの値を更新し、onChangeが値の変更を検知して、
変更があるたびに、クロージャ内の処理を実行していることがわかります。
SwiftUIでは、iOS15から@FocusStateというプロパティラッパーが利用できます。
フォーカスとは、「操作している対象」のことを指します。
@FocusStateを用いることで、フォーカスの有無を監視したり、制御したりすることができます。
使い方を見ていきましょう。
まず、Bool型のプロパティを「@FocusState」でラップして定義します。
struct ContentView: View {
@State var inputName = ""
@FocusState var isFocused: Bool // ⬅︎
var body: some View {
...
次に、監視したいView(今回はTextField)に、モディファイアの一種である、
「.focused($変数名)」を付与することで、対象のViewがフォーカスされているのか、いないのかを常に監視します。
TextField("名前を入力してください", text: $inputName)
.textFieldStyle(.roundedBorder)
.padding()
.focused($isFocused) // ⬅︎
フォーカスが検知されると、バインディングで紐付けたBool型変数に「true」が代入されます。フォーカスが外れると、「false」が代入されます。
✅Check!!!
「どうしてキーボードを閉じるのにフォーカスの制御が必要??」
1. TextFieldの入力ボックスをタップするとフォーカスが当たり、
キーボードが下から出現
⬇︎
2. かな文字入力の「改行」or ローマ字入力の「return」を押すと、
自動でキーボードが閉じる
この挙動は、TextFieldにデフォルトで搭載されている機能です。
しかし、以下画像のように、入力の終了を示すアクションボタンが個別に存在するアプリケーションも多くありますね。(Twitter,Instagramなど)
この場合、送信ボタンを押してもキーボードは自動で閉じてくれません。
これは、ButtonとTextFieldが別々のViewパーツであるため、TextFieldのデフォルト機能の実行範囲外であることが理由です。
この問題を解消するために、@FocusStateを使って対象Viewのフォーカス有無を意図的に監視制御することが必要になります。
最後に、Button(ボタン)とTextFieldを用いて、フォーカスの制御を実装してみましょう。
@FocusStateが付与されたBool型の変数プロパティを監視対象のViewパーツと紐付けすることで、
Bool型変数の値がtrue ⇨ 入力キーボードON
Bool型変数の値がfalse ⇨ 入力キーボードOFF
となるように実装します。
struct ButtonFocuseView: View {
@State private var inputName = ""
@FocusState var isFocused: Bool // ①
var body: some View {
VStack {
TextField("名前を入力してください", text: $inputName)
.textFieldStyle(.roundedBorder)
.padding()
.focused($isFocused) // ②
Button("送信") {
isFocused = false // ③
}
.onSubmit {
print("入力された値: \(inputName)")
}
// Bool変数isFocusedの更新を検知し、{}内の処理を実行
.onChange(of: isFocused, perform: { newValue in
print(newValue)
})
} // VStack
} // body
} // View
送信ボタンタップ時に自動でキーボードが閉じていますね!
@FocusState var isFocused: Bool // ①
フォーカスの状態を保持するためのBool型のプロパティです。
@FocusStateを付与することで、このプロパティの値によって、フォーカスがされているのか、されていないのか、判断を行います。
.focused($isFocused) // ②
.focused($変数)をフォーカスを監視したいViewに付与することで、
バインディング指定した変数の値の変化によってフォーカスの有無を切り替えることができるようになります。
ボタンのアクション(押した時の処理)クロージャ内で、Bool値プロパティをtrueからfalseに更新します。
これにより、ボタンを押すことでBool値がfalseに更新され、入力キーボードが閉じます。
TextFieldには、入力ボックスをタップした時点でフォーカスがONになり、
改行/returnを押すことでフォーカスOFFになるデフォルト機能が備わっていましたね。
こちらのボタンアクションで、フォーカスOFFの動きを補っているということです。
Button("送信") { isFocused = false } // ③
以上、SwiftUIの文字入力機能「TextField」の使い方をご紹介しました。
盛りだくさんの内容でしたが、どの要素もTextFieldを扱うにあたって重要なものなので、しっかりマスターして、開発に役立ててください!
\ SHARE /
アプリ開発が学べる勉強会を開催中!
CodeCandyではアプリ開発を学ぶための勉強会を定期開催しています。
学習する習慣を身につけたい、他の参加者と作業したい、アプリ開発の基本をマスターしたい、という方のために無料で学べる勉強会です。
グループにメンバー登録して頂くと、イベント開催時にメールで通知されます。
徹底した基礎学習からマスターするiPhoneアプリ開発集中オンライン講座開講!
本書「iPhoneアプリ開発集中講座」を執筆している現役エンジニア講師陣が直接に指導!
基礎、課題実習で実践力を鍛えて、オリジナルアプリ公開までチャレンジ!
充実した転職支援もあるので、エンジニアへ転職したい人にもおすすめです!
まずは、現役エンジニアに相談できる無料相談をご利用ください。
2022年2月よりSwift学習を始め、4月からiOSアプリ開発オンラインスクール「CodeCandy」にてアプリ開発を学ぶ。 2023年10月に個人開発アプリ「unico」をリリース。現在はアプリの機能アップデートをしながら、スクール運営の技術ブログの執筆や、出版書籍の入稿チェック・デバッグにも携わる。