🙄

【SwiftUI】TextFieldのタップエリアが狭すぎる

に公開1

SwiftUI Tipsです。

問題

このようなデザインのSwiftUIの入力フィールドを実装しました。

TextFieldの範囲に対して、ちょっとマージンがあって、枠線があるデザインです。
コードは↓こんな実装です。

struct MailInputView: View {
    @State var email: String = ""
    @FocusState private var focusedField: Field?
    enum Field: Hashable {
        case email
    }

    var body: some View {
        TextField("メールアドレス", text: $email)
            .font(.body)
            .keyboardType(.emailAddress)
            .padding(.horizontal, 8)
            .frame(height: 48)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(.gray, lineWidth: 1)
            )
            .padding()
            .focused($focusedField, equals: .email)
    }
}

これで最低限の機能は実現できるんですが、実機で触ってみると
「なんかタップ範囲やたら狭いな」という感覚がありました。
なぜそう感じたのかというと、タップ範囲が↓このエリアになるためです。

視覚的にはグレー線の中が全てタップ可能と感じますが、
実際はTextFieldがタップ範囲なので、実際は赤線の中になります。

対策

SwiftUIのタップ範囲について調べると、だいたい.contentShapeつけて、.onTapGestureをつけろというアドバイスが出てきます。
今回のケースでもこのアプローチしかなさそうです。
本当はTextFieldにタップ範囲広くできる何かがあると良かったのですが。

struct MailInputView: View {
    var body: some View {
        TextField("メールアドレス", text: $email)
            .font(.body)
            .keyboardType(.emailAddress)
            .padding(.horizontal, 8)
            .frame(height: 48)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(.gray, lineWidth: 1)
            )
+           .contentShape(.rect)         
+           .onTapGesture {              
+               focusedField = .email    
+           }                            
            .padding()
            .focused($focusedField, equals: .email)
    }
}

これで直感的にTextFieldが反応するようになりました。

おまけ

加えてですが、TextFieldの外をタップしたら、フォーカスを外すというのも欲しい動作です。
これはSwiftUIだとサラッと実現できました。

var body: some View {
    VStack {
        // …
    }
    .onTapGesture {
        focusedField = nil
    }
}

このようにonTapGestureを指定してあげると、いい感じにフォーカスが外れました。

(了)

Discussion

いちたかいちたか

TextFieldの枠をoverlayではなく、textFieldStyle で実現できます。
そうすることで、タップ範囲の懸念もonTapGestureの実装も不要かと思います。

TextField("メールアドレス", text: $email)
    .font(.body)
    .textFieldStyle(.roundedBorder) // これで枠を追加
    .keyboardType(.emailAddress)
    .padding()

自分のタップした感覚では問題なさそうに見えましたが、これでもタップ範囲が狭く感じる場合は、記事の通りの実装などを検討した方がいいかもです 🙏