如何使用 FocusedValue 键路径隐藏 TextFields 键盘 onTap?
How to hide TextFields keyboard onTap by using FocusedValue key path?
我正在尝试通过使用 iOS15 (SwiftUI 3) 随附的新 .focused() 方法在屏幕上任何位置的 .onTap 时实现隐藏键盘。
首先声明我的 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 }
}
}
然后在 MainApp 中,当我点击屏幕中的任意位置时,我将 FocusState 设置为将 nil 传递给路径。
struct TextFieldTestApp: App {
@FocusState private var state: FocusedTextField.Field?
var body: some Scene {
WindowGroup {
ContentView()
.onTapGesture { state = nil }
.focusedValue(\.textField, $state)
}
}
}
然后我创建我的 ContentView
struct ContentView: View {
@State private var firstName = ""
@State private var lastName = ""
@State private var emailAddress = ""
// Error: Key path value type 'FocusedTextField.Value?' (aka 'Optional<FocusState<FocusedTextField.Field>.Binding>')
// cannot be converted to contextual type 'Binding<Value>?'
@FocusedBinding(\.textField) var focusedTextField
var body: some View {
ZStack {
Color.red
VStack {
Spacer()
TextField("Enter your first name", text: $firstName)
// Error: Type 'Hashable' has no member 'firstName'
.focused($focusedTextField, equals: .firstName)
.textContentType(.givenName)
.submitLabel(.next)
TextField("Enter your last name", text: $lastName)
// Error: Type 'Hashable' has no member 'lastName'
.focused(focusedTextField, equals: .lastName)
.textContentType(.familyName)
.submitLabel(.next)
TextField("Enter your email address", text: $emailAddress)
// Error: Type 'Hashable' has no member 'emailAddress'
.focused(focusedTextField, equals: .emailAddress)
.textContentType(.emailAddress)
.submitLabel(.join)
Spacer()
}
}
}
}
我也尝试过使用环境变量,但@FocusState PropertyWrapper 再次导致问题。
A FocusedValueKey
用于已更改的值(例如,如果您想传递输入的文本或状态值),绑定到聚焦状态未更改且不符合概念(因此签名不匹配) .
据我所知,您的目标可以通过直接将聚焦状态注入 ContentView
.
来实现
这是一种方法(w/o 大量重构您的代码)。测试 Xcode 13 / iOS 15
struct TextFieldTestApp: App {
@FocusState private var state: FocusedTextField.Field?
var body: some Scene {
WindowGroup {
ContentView(focusedTextField: $state)
.onTapGesture { state = nil }
}
}
}
struct ContentView: View {
@State private var firstName = ""
@State private var lastName = ""
@State private var emailAddress = ""
var focusedTextField: FocusState<FocusedTextField.Field?>.Binding
var body: some View {
ZStack {
Color.red
VStack {
Spacer()
TextField("Enter your first name", text: $firstName)
.focused(focusedTextField, equals: .firstName)
.textContentType(.givenName)
.submitLabel(.next)
TextField("Enter your last name", text: $lastName)
.focused(focusedTextField, equals: .lastName)
.textContentType(.familyName)
.submitLabel(.next)
TextField("Enter your email address", text: $emailAddress)
.focused(focusedTextField, equals: .emailAddress)
.textContentType(.emailAddress)
.submitLabel(.join)
Spacer()
}
}
}
}
更新:
这是一种基于 EnvironmentValues
的方法。在相同的环境中测试。
struct TextFieldTestApp: App {
@FocusState private var state: FocusedTextField.Field?
var body: some Scene {
WindowGroup {
ContentView()
.onTapGesture { state = nil }
.environment(\.textField, $state)
}
}
}
struct FocusedTextField: EnvironmentKey {
static var defaultValue: FocusState<Field?>.Binding? = nil
enum Field: Hashable {
case firstName
case lastName
case emailAddress
}
typealias Value = FocusState<Field?>.Binding?
}
extension EnvironmentValues {
var textField: FocusedTextField.Value {
get { self[FocusedTextField.self] }
set { self[FocusedTextField.self] = newValue }
}
}
struct ContentView: View {
@State private var firstName = ""
@State private var lastName = ""
@State private var emailAddress = ""
@Environment(\.textField) private var focusedTextField
var body: some View {
ZStack {
Color.red
VStack {
Spacer()
TextField("Enter your first name", text: $firstName)
.focused(focusedTextField!, equals: .firstName)
.textContentType(.givenName)
.submitLabel(.next)
TextField("Enter your last name", text: $lastName)
.focused(focusedTextField!, equals: .lastName)
.textContentType(.familyName)
.submitLabel(.next)
TextField("Enter your email address", text: $emailAddress)
.focused(focusedTextField!, equals: .emailAddress)
.textContentType(.emailAddress)
.submitLabel(.join)
Spacer()
}
}
}
}
注意:为了演示简单,环境值是强制展开的,但对于实际项目,最好有条件地检查值是否存在,如果环境不为零则添加修饰符。
我正在尝试通过使用 iOS15 (SwiftUI 3) 随附的新 .focused() 方法在屏幕上任何位置的 .onTap 时实现隐藏键盘。
首先声明我的 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 }
}
}
然后在 MainApp 中,当我点击屏幕中的任意位置时,我将 FocusState 设置为将 nil 传递给路径。
struct TextFieldTestApp: App {
@FocusState private var state: FocusedTextField.Field?
var body: some Scene {
WindowGroup {
ContentView()
.onTapGesture { state = nil }
.focusedValue(\.textField, $state)
}
}
}
然后我创建我的 ContentView
struct ContentView: View {
@State private var firstName = ""
@State private var lastName = ""
@State private var emailAddress = ""
// Error: Key path value type 'FocusedTextField.Value?' (aka 'Optional<FocusState<FocusedTextField.Field>.Binding>')
// cannot be converted to contextual type 'Binding<Value>?'
@FocusedBinding(\.textField) var focusedTextField
var body: some View {
ZStack {
Color.red
VStack {
Spacer()
TextField("Enter your first name", text: $firstName)
// Error: Type 'Hashable' has no member 'firstName'
.focused($focusedTextField, equals: .firstName)
.textContentType(.givenName)
.submitLabel(.next)
TextField("Enter your last name", text: $lastName)
// Error: Type 'Hashable' has no member 'lastName'
.focused(focusedTextField, equals: .lastName)
.textContentType(.familyName)
.submitLabel(.next)
TextField("Enter your email address", text: $emailAddress)
// Error: Type 'Hashable' has no member 'emailAddress'
.focused(focusedTextField, equals: .emailAddress)
.textContentType(.emailAddress)
.submitLabel(.join)
Spacer()
}
}
}
}
我也尝试过使用环境变量,但@FocusState PropertyWrapper 再次导致问题。
A FocusedValueKey
用于已更改的值(例如,如果您想传递输入的文本或状态值),绑定到聚焦状态未更改且不符合概念(因此签名不匹配) .
据我所知,您的目标可以通过直接将聚焦状态注入 ContentView
.
这是一种方法(w/o 大量重构您的代码)。测试 Xcode 13 / iOS 15
struct TextFieldTestApp: App {
@FocusState private var state: FocusedTextField.Field?
var body: some Scene {
WindowGroup {
ContentView(focusedTextField: $state)
.onTapGesture { state = nil }
}
}
}
struct ContentView: View {
@State private var firstName = ""
@State private var lastName = ""
@State private var emailAddress = ""
var focusedTextField: FocusState<FocusedTextField.Field?>.Binding
var body: some View {
ZStack {
Color.red
VStack {
Spacer()
TextField("Enter your first name", text: $firstName)
.focused(focusedTextField, equals: .firstName)
.textContentType(.givenName)
.submitLabel(.next)
TextField("Enter your last name", text: $lastName)
.focused(focusedTextField, equals: .lastName)
.textContentType(.familyName)
.submitLabel(.next)
TextField("Enter your email address", text: $emailAddress)
.focused(focusedTextField, equals: .emailAddress)
.textContentType(.emailAddress)
.submitLabel(.join)
Spacer()
}
}
}
}
更新:
这是一种基于 EnvironmentValues
的方法。在相同的环境中测试。
struct TextFieldTestApp: App {
@FocusState private var state: FocusedTextField.Field?
var body: some Scene {
WindowGroup {
ContentView()
.onTapGesture { state = nil }
.environment(\.textField, $state)
}
}
}
struct FocusedTextField: EnvironmentKey {
static var defaultValue: FocusState<Field?>.Binding? = nil
enum Field: Hashable {
case firstName
case lastName
case emailAddress
}
typealias Value = FocusState<Field?>.Binding?
}
extension EnvironmentValues {
var textField: FocusedTextField.Value {
get { self[FocusedTextField.self] }
set { self[FocusedTextField.self] = newValue }
}
}
struct ContentView: View {
@State private var firstName = ""
@State private var lastName = ""
@State private var emailAddress = ""
@Environment(\.textField) private var focusedTextField
var body: some View {
ZStack {
Color.red
VStack {
Spacer()
TextField("Enter your first name", text: $firstName)
.focused(focusedTextField!, equals: .firstName)
.textContentType(.givenName)
.submitLabel(.next)
TextField("Enter your last name", text: $lastName)
.focused(focusedTextField!, equals: .lastName)
.textContentType(.familyName)
.submitLabel(.next)
TextField("Enter your email address", text: $emailAddress)
.focused(focusedTextField!, equals: .emailAddress)
.textContentType(.emailAddress)
.submitLabel(.join)
Spacer()
}
}
}
}
注意:为了演示简单,环境值是强制展开的,但对于实际项目,最好有条件地检查值是否存在,如果环境不为零则添加修饰符。