Issue
I am trying to achieve hiding keyboard when .onTap anywhere in screen by using new .focused() methods which came with iOS15 (SwiftUI 3).
First declare my 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 }
}
}
then in MainApp, I put FocusState to pass nil to the path when I tapped anywhere in screen.
struct TextFieldTestApp: App {
@FocusState private var state: FocusedTextField.Field?
var body: some Scene {
WindowGroup {
ContentView()
.onTapGesture { state = nil }
.focusedValue(\.textField, $state)
}
}
}
Then I create my 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()
}
}
}
}
I also tried it with environment variables but @FocusState PropertyWrapper causes problems again.
Solution
A FocusedValueKey
is for changed value (ex. if you would want to pass entered text or state value), binding to focused state is not changed and does not fit concept (so signature is not matched).
As far as I can understand your goal it can be achieved by direct focused state injection into ContentView
.
Here is an approach (w/o much refactoring of your code). Tested with 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()
}
}
}
}
Update:
Here is an approach based on EnvironmentValues
. Tested in same environment.
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()
}
}
}
}
Note: for demo simplicity environment value is force-unwrapped, but for real project it is better to check conditionally if value is present and add modifier if environment is not nil.
Answered By - Asperi Answer Checked By - Terry (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.