如何通过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
相关产品推荐
相关产品推荐

