Reputation: 31
Hi everyone,
I have a parentview with multiple childviews that contain multiple TextFields.
I want to add chevrons to the keyboard toolbar to easily move the focus to the next TextField, but I don't know how I can access the FocusState of the childviews.
Is there perhaps a way to get the view that corresponds to the focus identifier (?, don't know what to call it)?
struct ParentView: View {
@FocusState var focusedChild: Int?
var body: some View {
ForEach(1..<5) { index in
ChildView(index: index)
.focused($focusedChild, equals: index)
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button {
// focusPrevious() <--
// Is there a way to access the view associated with the index or the current focusedChild?
// ChildView[index].focusState = ChildView[index].focusState + 1 % 3
} label: {
Image(systemName: "chevron.left")
}
Button {
//
} label: {
Image(systemName: "chevron.right")
}
Spacer()
Button {
focusedChild = nil
} label: {
Image(systemName: "keyboard.chevron.compact.down")
}
}
}
}
}
struct ChildView: View {
var index: Int
@State var field1: String = ""
@State var field2: String = ""
@State var field3: String = ""
@FocusState var focusedField: Int?
var body: some View {
VStack {
Text(String(index))
TextField("Field 1", text: $field1)
.focused($focusedField, equals: 1)
TextField("Field 2", text: $field2)
.focused($focusedField, equals: 2)
TextField("Field 3", text: $field3)
.focused($focusedField, equals: 3)
}
}
}
I tried to add the Toolbar to the ChildView but that results in a pair of chevrons for every childView (in this case 3 pairs of chevrons), instead of just one pair.
Any help would be greatly appreciated, thanks!
Upvotes: 2
Views: 1083
Reputation: 273860
Since the child views already takes an index
, just assign different focused states depending on that. If you do this in a way such that they are consecutive numbers, “going to the next/previous text field” would be trivial. You'd assign them index * 3
, index * 3 + 1
and index * 3 + 2
.
Rather than having a FocusState
in each child view, the child views should take a FocusState<Int?>.Binding
(like a regular Binding
, but for focus states).
struct ChildView: View {
let index: Int
@State var field1: String = ""
@State var field2: String = ""
@State var field3: String = ""
let focusedField: FocusState<Int?>.Binding
var body: some View {
VStack {
Text(String(index))
TextField("Field 1", text: $field1)
.focused(focusedField, equals: index * 3)
TextField("Field 2", text: $field2)
.focused(focusedField, equals: index * 3 + 1)
TextField("Field 3", text: $field3)
.focused(focusedField, equals: index * 3 + 2)
}
}
}
For a more flexible approach, ChildView
could just take the focus states of all three text fields, like
let textField1Focus: Int
let textField2Focus: Int
let textField3Focus: Int
In any case, now you should remove the .focused
modifier on ChildView
s:
ForEach(0..<4) { index in
ChildView(index: index, focusedField: $focusedChild)
}
Then the forwards and backwards button would just be:
Button {
if let focusedChild, focusedChild > 0 {
// be careful here, self.focusedChild and focusedChild are not the same thing!
// focusedChild is the unwrapped value!
self.focusedChild? -= 1
}
} label: {
Image(systemName: "chevron.left")
}
Button {
if let focusedChild, focusedChild < 11 {
self.focusedChild? += 1
}
} label: {
Image(systemName: "chevron.right")
}
Upvotes: 1