【SwiftUI】TextFieldについて

# はじめに

SwiftUIのTextFieldの実装方法についてドキュメントを読んで調べてみました。
（ついでにTextEditor、SecureFieldも）

https://developer.apple.com/documentation/swiftui/textfield

# 環境
Xcode 13.4.1

# 内容

- TextField
- TextEditor
- SecureField

## TextField
編集可能なテキストインターフェイスを表示

```swift
struct TextField<Label> where Label : View
```

ラベルと値へのバインドを持つテキストフィールドを作成する。値が文字列の場合、ユーザーがフィールド内のテキストを入力または編集すると、テキストフィールドはこの値を継続的に更新、文字列でない場合は、ユーザーがReturnキーを押すなどして編集をコミットすると値が更新される

```swift
@State private var username: String = ""
@FocusState private var emailFieldIsFocused: Bool

var body: some View {
    TextField(
        "User name (email address)",
        text: $username
    )
    .focused($emailFieldIsFocused)
    .onSubmit {
        // validate(name: username)
    }
    .textInputAutocapitalization(.never)
    .disableAutocorrection(true)
    .border(.secondary)

    Text(username)
        .foregroundColor(emailFieldIsFocused ? .red : .blue)
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/4684b790-f485-e55b-874f-d0624d6586d8.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/f19102f7-f70d-df1e-6783-eea131d41116.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/d14d4c3f-aadd-255e-be6f-7f79cad59e8f.png">

さらに、`FormatStyle`を使用すると、テキストフィールドを非文字列型にバインドし、入力されたテキストをバインドされた型のインスタンスに変換することができる。
例えば、`PersonNameComponents.FormatStyle`を使用して、テキスト フィールドに入力された名前を `PersonNameComponents`インスタンスに変換するなど

```swift
@State private var nameComponents = PersonNameComponents()

var body: some View {
    TextField(
        "Proper name",
        value: $nameComponents,
        format: .name(style: .medium)
    )
    .onSubmit {
        // validate(components: nameComponents)
    }
    .disableAutocorrection(true)
    .border(.secondary)
    Text(nameComponents.debugDescription)
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/a9bcc484-456f-99fc-2e85-cf1c749c2398.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/74a63015-6294-a2d5-860e-a41a62386ceb.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/86c5ae2e-b1aa-9f28-a184-7fb1a80866ce.png">

##### `TextField`のスタイル設定

`TextFieldStyle`のインスタンスを渡して`textFieldStyle(_:)`を使えば、外観や動作をカスタマイズすることができる

```swift
@State private var givenName: String = ""
@State private var familyName: String = ""

var body: some View {
    VStack {
        TextField(
            "Given Name",
            text: $givenName
        )
        .disableAutocorrection(true)
        TextField(
            "Family Name",
            text: $familyName
        )
        .disableAutocorrection(true)
    }
    .textFieldStyle(.roundedBorder)
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/70137794-954f-e605-5ecd-7e40b28b1bfa.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/0361e39c-f74f-6c19-7b53-23d142d6bc7a.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/08931b3e-8728-be53-15f5-fa5712f95f1b.png">


### TextEditor

https://developer.apple.com/documentation/swiftui/texteditor

長文テキストを表示・編集することができる

```swift
struct TextEditor
```

テキストエディタViewを使用すると、アプリのユーザーインターフェイスに複数行のスクロール可能なテキストを表示したり編集したりすることができる

```swift
@State private var fullText: String = "This is some editable text..."

var body: some View {
    TextEditor(text: $fullText)
        .foregroundColor(Color.gray)
        .font(.custom("HelveticaNeue", size: 13))
        .lineSpacing(5)
}

```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/9a33dac5-bfdb-c2cd-221a-b9a1afebffc4.png">

### SecureField

https://developer.apple.com/documentation/swiftui/securefield

`TextField`と同様の動作をさせたいが、ユーザーのテキストは表示したくない(パスワードなどの機密情報)場合に使用

```swift
struct SecureField<Label> where Label : View
```

`SecureField`では、文字列値へのバインドと、ユーザがReturnキーを押すなどして編集をコミットしたときに実行されるクロージャが使用される。フィールドは、キーストロークなどの編集のたびにバインドされた文字列を更新するので、Doneボタンなどの他のコントロールからいつでもその値を読み取ることができる

```swift
@State private var username: String = ""
@State private var password: String = ""

var body: some View {
    TextField(
        "User name (email address)",
        text: $username)
        .autocapitalization(.none)
        .disableAutocorrection(true)
        .border(Color(UIColor.separator))
    SecureField(
        "Password",
        text: $password
    ) {
        // handleLogin(username: username, password: password)
    }
    .border(Color(UIColor.separator))
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/6eb5dd5e-ab36-3946-f0e5-a9f880a2a6a6.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/c5c36f22-0f33-4a51-d3ff-baf99c49c29f.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/7628b502-33a2-a433-6303-0212acff7de3.png">

# おまけ
ここからはドキュメントの内容から少し離れます。。。
### 改行のできる`TextField`を作成する
こちらはiOS16から使用でき、まだ現状はベータ版のようですが
`TextField`だけで改行ができ、さらに行数を指定することで枠の最大も決められて便利そう

```swift
@State private var text: String = ""

var body: some View {
    TextField("Title", text: $text,  axis: .vertical)
        .lineLimit(0...10)
        .border(.secondary)
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/4da2842e-a6c3-cf2d-26c8-cf6ea6f078ce.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/6c02bab6-2848-1fac-1d95-311422437734.png"><img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/b906b060-d51a-8fe4-33a5-455fdfba1382.png">

#### `TextField`は使わず、`TextEditor`を使う

iOS15では`TextField`を使うと改行ができないので、
`TextEditor`を使って実現します

```swift
@State private var text: String = ""

var body: some View {
    List {
        Text("Enter the message")
            
        ZStack {
            TextEditor(text: $text)
            Text(text).opacity(0).padding(.all, 8)
        }
        .shadow(radius: 1)
    }
    .listStyle(.plain)
}
```

<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/c807ca21-47e9-e21d-814f-7c9e0267e755.png">
<img width="200" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/2cd9a2b2-8a8f-56ab-869d-91d73db9b5eb.png">


# おわりに

iOS16からは`TextField`で改行ができるようになるので、メッセージやコメント機能がより簡単に実装できそうでとても良さそうです🙌

# 参考

https://developer.apple.com/documentation/swiftui/textfield

https://stackoverflow.com/questions/56471973/how-do-i-create-a-multiline-textfield-in-swiftui

https://sarunw.com/posts/swiftui-multiline-textfield/