You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过FocusedValue键路径实现点击屏幕隐藏SwiftUI TextField键盘

问题原因与修复方案

错误根源

  • 第一类类型不匹配错误:你自定义的FocusedValueKey存储的值本身就是FocusState<Field>.Binding类型,不需要用@FocusedBinding包装(该属性包装器会自动对值做一层Binding转换,导致类型叠加不匹配),应该直接用@Environment读取对应的环境值
  • 第二类枚举成员找不到错误:因为focusedTextField没有明确的类型声明,编译器无法推断.firstName等归属的枚举类型,所以报错提示Hashable没有对应成员

修正后的完整代码

1、自定义FocusedValueKey部分无需修改

struct FocusedTextField: FocusedValueKey {
    enum Field: Hashable {
        case firstName
        case lastName
        case emailAddress
    }
    typealias Value = FocusState<Field>.Binding
}

extension FocusedValues {
    var textField: FocusedTextField.Value? {
        get { self[FocusedTextField.self] }
        set { self[FocusedTextField.self] = newValue }
    }
}

2、App入口部分无需修改

struct TextFieldTestApp: App {
    @FocusState private var state: FocusedTextField.Field?
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onTapGesture { state = nil }
                .focusedValue(\.textField, $state)
        }
    }
}

3、修正后的ContentView代码

struct ContentView: View {
    @State private var firstName = ""
    @State private var lastName = ""
    @State private var emailAddress = ""
    // 替换@FocusedBinding为@Environment,明确指定类型
    @Environment(\.textField) var focusedTextField

    var body: some View {
        ZStack {
            Color.red.ignoresSafeArea()
            VStack {
                Spacer()
                TextField("Enter your first name", text: $firstName)
                    // 解包可选值,明确指定枚举归属
                    .focused(focusedTextField, equals: FocusedTextField.Field.firstName)
                    .textContentType(.givenName)
                    .submitLabel(.next)

                TextField("Enter your last name", text: $lastName)
                    .focused(focusedTextField, equals: FocusedTextField.Field.lastName)
                    .textContentType(.familyName)
                    .submitLabel(.next)

                TextField("Enter your email address", text: $emailAddress)
                    .focused(focusedTextField, equals: FocusedTextField.Field.emailAddress)
                    .textContentType(.emailAddress)
                    .submitLabel(.join)
                Spacer()
            }
            .padding()
        }
    }
}

更简化的实现方案(无需自定义FocusedValueKey)

如果你的视图层级不复杂,不需要跨多层传递FocusState,可以直接在ContentView内部处理,代码更简洁:

struct ContentView: View {
    @State private var firstName = ""
    @State private var lastName = ""
    @State private var emailAddress = ""
    @FocusState private var focusedField: Field?
    
    enum Field: Hashable {
        case firstName, lastName, emailAddress
    }

    var body: some View {
        ZStack {
            Color.red.ignoresSafeArea()
                .onTapGesture { focusedField = nil }
            VStack {
                Spacer()
                TextField("Enter your first name", text: $firstName)
                    .focused($focusedField, equals: .firstName)
                    .textContentType(.givenName)
                    .submitLabel(.next)
                TextField("Enter your last name", text: $lastName)
                    .focused($focusedField, equals: .lastName)
                    .textContentType(.familyName)
                    .submitLabel(.next)
                TextField("Enter your email address", text: $emailAddress)
                    .focused($focusedField, equals: .emailAddress)
                    .textContentType(.emailAddress)
                    .submitLabel(.join)
                Spacer()
            }
            .padding()
        }
    }
}

内容的提问来源于stack exchange,提问作者OguzYuksel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 18:27:03