SwiftUI TextFieldStyle の作成
TextFieldStyle プロトコルを使うと、テキスト入力フィールドの見た目をカスタマイズできる。標準の .roundedBorder では物足りない場合に、独自のデザインを適用できる。
TextFieldStyle の基本
TextFieldStyle プロトコルは _body(configuration:) メソッドを実装する。
struct UnderlineTextFieldStyle: TextFieldStyle {
func _body(configuration: TextField<_Label>) -> some View {
configuration
.padding(.vertical, 8)
.overlay(
Rectangle()
.frame(height: 1)
.foregroundColor(.gray),
alignment: .bottom
)
}
}configuration はそのまま TextField として扱える。これに装飾を追加して返す。
TextField("メールアドレス", text: $email)
.textFieldStyle(UnderlineTextFieldStyle())フォーカス状態の反映
@FocusState と組み合わせて、フォーカス時に見た目を変えるスタイルを作る。
struct FocusableTextFieldStyle: TextFieldStyle {
@FocusState private var isFocused: Bool
func _body(configuration: TextField<_Label>) -> some View {
configuration
.focused($isFocused)
.padding(12)
.background(
RoundedRectangle(cornerRadius: 8)
.stroke(isFocused ? Color.blue : Color.gray.opacity(0.3), lineWidth: isFocused ? 2 : 1)
)
}
}フォーカス時にボーダーが青く太くなる。
ラベル付きスタイル
フィールドの上にラベルを表示するスタイル。
struct LabeledTextFieldStyle: TextFieldStyle {
var label: String
func _body(configuration: TextField<_Label>) -> some View {
VStack(alignment: .leading, spacing: 4) {
Text(label)
.font(.caption)
.foregroundColor(.secondary)
configuration
.padding(12)
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
}
}
}
extension TextFieldStyle where Self == LabeledTextFieldStyle {
static func labeled(_ label: String) -> LabeledTextFieldStyle {
LabeledTextFieldStyle(label: label)
}
}TextField("", text: $username)
.textFieldStyle(.labeled("ユーザー名"))アイコン付きスタイル
struct IconTextFieldStyle: TextFieldStyle {
var icon: String
func _body(configuration: TextField<_Label>) -> some View {
HStack(spacing: 8) {
Image(systemName: icon)
.foregroundColor(.gray)
configuration
}
.padding(12)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.1))
)
}
}
extension TextFieldStyle where Self == IconTextFieldStyle {
static func icon(_ systemName: String) -> IconTextFieldStyle {
IconTextFieldStyle(icon: systemName)
}
}TextField("検索", text: $searchText)
.textFieldStyle(.icon("magnifyingglass"))
TextField("メール", text: $email)
.textFieldStyle(.icon("envelope"))バリデーション対応スタイル
struct ValidatedTextFieldStyle: TextFieldStyle {
var isValid: Bool?
func _body(configuration: TextField<_Label>) -> some View {
HStack {
configuration
if let isValid = isValid {
Image(systemName: isValid ? "checkmark.circle.fill" : "xmark.circle.fill")
.foregroundColor(isValid ? .green : .red)
}
}
.padding(12)
.background(
RoundedRectangle(cornerRadius: 8)
.stroke(borderColor, lineWidth: 1)
)
}
private var borderColor: Color {
guard let isValid = isValid else { return .gray.opacity(0.3) }
return isValid ? .green : .red
}
}入力のバリデーション結果を視覚的にフィードバックできる。
マテリアルデザイン風スタイル
struct MaterialTextFieldStyle: TextFieldStyle {
var label: String
@FocusState private var isFocused: Bool
@State private var hasText: Bool = false
func _body(configuration: TextField<_Label>) -> some View {
ZStack(alignment: .leading) {
Text(label)
.foregroundColor(isFocused ? .blue : .gray)
.font(isFocused || hasText ? .caption : .body)
.offset(y: isFocused || hasText ? -20 : 0)
.animation(.easeInOut(duration: 0.2), value: isFocused)
configuration
.focused($isFocused)
.onChange(of: isFocused) { _ in
// テキストの有無を確認するロジックが必要
}
}
.padding(.top, 16)
.overlay(
Rectangle()
.frame(height: isFocused ? 2 : 1)
.foregroundColor(isFocused ? .blue : .gray),
alignment: .bottom
)
}
}フォーカス時にラベルが上に移動する、マテリアルデザインのようなアニメーションを実現できる。
標準スタイルとの比較
.roundedBorder
最もシンプル。設定画面やフォームで広く使える。
.plain
装飾なし。カスタムデザインのベースとして使う。
TextFieldStyle はアンダースコア付きの _body メソッドを使用するため、将来の API 変更に注意が必要だ。安定性を重視する場合は、TextField を直接ラップするカスタムビューを作成する方法も検討に値する。












