端的にいうと
SwiftUI100行チャレンジ エピソード7。TextFieldの使用例。
1) TextField概要
SwiftUIから提供されているプレーンな文字入力ウィジット。UIKit(iOS SDKのテキスト入力フィールド)のUITextFieldと名称が似ており同機能を提供している印象があるが継承されている機能は限定的(キーボード形式、キーボード自動Off条件など)でできる点だけ見るとWebでのFormのテキスト入力に近い印象がある。
2) サンプルコードについて
サンプルコードはTextFieldのスタイルShowcaseとなっている。SwiftUIが用意しているスタイルとカスタマイズスタイルを確認することができる。
import SwiftUI | |
enum TextFieldTypes { | |
case defaultStyle | |
case customStyle | |
} | |
struct ContentView: View { | |
@State var myfield = "" | |
@ScaledMetric(relativeTo: .body) var imageSize = CGFloat(2) | |
@ScaledMetric(relativeTo: .body) var horizontalPadding: CGFloat = 4 | |
@ScaledMetric(relativeTo: .body) var verticalPadding: CGFloat = 2 | |
@ScaledMetric(relativeTo: .body) var cornerRadiusValue: CGFloat = 8 | |
@State var textFieldTypes: TextFieldTypes = .defaultStyle | |
@State var text = "" | |
var body: some View { | |
VStack { | |
Picker("TextFieldStyle", selection: $textFieldTypes) { | |
Text("Original").tag(TextFieldTypes.defaultStyle) | |
Text("Custom").tag(TextFieldTypes.customStyle) | |
} | |
.pickerStyle(SegmentedPickerStyle()) | |
.padding() | |
switch textFieldTypes { | |
case .defaultStyle: | |
HStack { | |
Text("DefaultTextField") | |
Spacer() | |
}.padding(.horizontal) | |
TextField("Label", text: $myfield) | |
.textFieldStyle(RoundedBorderTextFieldStyle()) | |
.padding(.horizontal) | |
HStack { | |
Text("Plain") | |
Spacer() | |
}.padding(.horizontal) | |
TextField("Label", text: $myfield) | |
.textFieldStyle(DefaultTextFieldStyle()) | |
.padding(.horizontal) | |
case .customStyle: | |
HStack { | |
Text("Rect") | |
Spacer() | |
}.padding(.horizontal) | |
TextField("Label", text: $myfield) | |
.padding(EdgeInsets(top: verticalPadding, leading: horizontalPadding, bottom: verticalPadding, trailing: horizontalPadding)) | |
.background( | |
Rectangle() | |
.stroke(Color.gray, lineWidth: 0.333) | |
) | |
.padding(.horizontal) | |
HStack { | |
Text("RoundRect") | |
Spacer() | |
}.padding(.horizontal) | |
TextField("Label", text: $myfield) | |
.padding(EdgeInsets(top: verticalPadding, | |
leading: horizontalPadding + cornerRadiusValue * 0.5, | |
bottom: verticalPadding, | |
trailing: horizontalPadding + cornerRadiusValue * 0.5)) | |
.background( | |
RoundedRectangle(cornerRadius: cornerRadiusValue) | |
.stroke(Color.gray, lineWidth: 0.333) | |
.foregroundColor(.clear) | |
) | |
.padding(.horizontal) | |
} | |
Spacer() | |
} | |
} | |
} |
サンプルコードの試し方はXcodeでプロジェクト新規作成 - iOS Appもしくはmulti platformを選択、プロジェクトのオプションでUIでSwiftUIを選択しプロジェクト作成後、Xcodeで自動生成されたContentView.swift をサンプルコードで置き換えて実行で確認できる。
3) サンプルコード解説
TextFieldの仕組みとスタイルの指定方法について説明する。
3-1) テキストフィールドと変更文字列を関連づける
TextFieldは定義時にペアとなる文字列を指定する。指定に際してSwiftUIではBindと呼ばれる仕組みを使っている。Bindという仕組みは存在しているがSwiftUIを使う側として考えるのは2点、
- 文字列を指定する変数名に$記号をつける
- 利用可能な変数属性8種から適切な定義を指定する
が重要になる。利用可能な変数属性は以下となる。
- @State
- ObservableObjectの変数で使用できる@Publish
- @ObservedObject
- @EnvironmentObject
- @StateObject
- @AppStorage
- @SceneStorage
- @Binding
上記変数属性8種はSwiftUI framework独自の定義となっている。@〜定義をSwiftUI独自に用意できているかについては大枠でSwift Attributeについて説明した際に一部取り上げているので参考にしてほしい。
プレゼン資料やりたい放題 Swift Attributes https://irimasu.com/apple-swift-attributes
サンプルコードではView上で@Stateを指定している。変数属性は変数定義前に付与する記述となっている。
@State var myfield = ""
3-2) テキストフィールドのスタイルを変更する
SwiftUIのウィジットはWbeのCSSに近しいウィジットへの装飾が可能となっている。TextFieldも例外ではなく入力枠をカスタマイズすることができる。
SwiftUIのTextFieldでの入力フィールドのスタイルはiOS14 では未指定もしくはOS依存の枠しかないためカスタムな入力欄を用意するにはウィジットへの装飾方法を理解することが重要になってくる。
サンプルコードでは矩形枠と角丸矩形枠のカスタム入力欄を用意しているが画面サイズが異なるiPhone上でもレイアウト崩れを起こしにくくするため@ScaledMetric を使用している。
@ScaledMetricは数値(正確にはBinaryFloatingPoint準拠の型)を画面サイズに連動させるために指定する属性となっている。
@ScaledMetric(relativeTo: .body) var imageSize = CGFloat(2)
@ScaledMetricは連動させるために参照する対象を指定することができる。参照先は文字列の各種スタイルを指定できる。.bodyは標準的な文字列サイズとなっている。
まとめ
TextFieldはSwiftUIの利用例として紹介される頻度が多いウィジットである。
TextFieldは使う側の経歴によって印象が変わる傾向がある。UIKitからのiOSアプリ開発者でUITextFieldを使う側からみればSwiftUIのTextFieldは機能面の制限、スタイルの指定方法が大きく変わったため違和感が大きい。
一方Web開発者から見るとFormのテキスト入力に近い感覚で使用できる印象がある(Web開発がSwiftUIを積極使うかは別として)。
サンプルではカスタムレイアウトは@ScaledMetric の使用方法を強調するため装飾はシンプルなものに留めている。入力欄をブラーやバイブランシーでレイアウトすることもミドルウェアを導入すると可能となるので興味がある方は試してみて欲しい。
SwiftUIのTextFieldではPlaceHolderのスタイル調整が難しいのでカスタマイズ必要はある。