#ios #swiftui #environment-variables
Вопрос:
Я пытаюсь добиться скрытия клавиатуры, когда .onTap в любом месте экрана с помощью новых методов .focused (), которые поставляются с iOS15 (SwiftUI 3).
Сначала объявите мою сфокусированную ценность
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, чтобы передать ноль в путь, когда я нажимал в любом месте экрана.
struct TextFieldTestApp: App {
@FocusState private var state: FocusedTextField.Field?
var body: some Scene {
WindowGroup {
ContentView()
.onTapGesture { state = nil }
.focusedValue(.textField, $state)
}
}
}
Затем я создаю свое представление контента
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 снова вызывает проблемы.
Ответ №1:
A FocusedValueKey
для измененного значения (например, если вы хотите передать введенный текст или значение состояния), привязка к сфокусированному состоянию не изменяется и не соответствует концепции (поэтому подпись не соответствует).
Насколько я могу понять вашу цель, она может быть достигнута путем прямого целенаправленного введения состояния в ContentView
.
Вот подход (без значительного рефакторинга вашего кода). Протестировано с 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()
}
}
}
}
Примечание: для простоты демонстрации значение среды принудительно раскрывается, но для реального проекта лучше условно проверить, присутствует ли значение, и добавить модификатор, если среда не равна нулю.
Комментарии:
1. Внедрение будет беспорядочным во вложенных представлениях, вместо этого я попытался найти решение для свойств среды, которое может применяться ко всем экранам в приложении.
2. Подход, основанный на
EnvironmentValues
этом, не работает.