I have a view called TitleList
that allows the user to edit the title of a row using a .swipeAction
(it will replace the NavigationLink
with a TextField
).
While a title is being edited, if a user taps another row's NavigationLink
and a view gets pushed on the root views NavigationStack
, when I return to the root view and try to edit the previous row again, its TextField
doesn't automatically activate despite its FocusState
property being set to true (I have to tap the field for the keyboard to pop up). Here is a video that shows what I'm describing and also here is the full code:
import SwiftUI
struct HomeView: View {
@State private var titles: [String] = ["Title 1", "Title 2", "Title 3"]
var body: some View {
NavigationStack {
VStack {
List {
TitleList(titles: $titles)
}
}
}
}
}
struct TitleList: View {
@Binding var titles: [String]
@State private var renamingTitleIndex: Int?
@State private var renamingTextFieldUserInput: String = ""
@FocusState private var renamingTextFieldIsFocused: Bool
var body: some View {
ForEach(Array(titles.enumerated()), id: \.offset) { index, title in
if renamingTitleIndex != index {
NavigationLink(destination: Text("Test")) {
Text(titles[index])
}
.swipeActions(allowsFullSwipe: false) {
Button {
renamingTextFieldIsFocused = true
renamingTitleIndex = index
renamingTextFieldUserInput = ""
} label: {
Label("Rename", systemImage: "pencil")
}
}
} else {
VStack {
TextField("", text: $renamingTextFieldUserInput)
.focused($renamingTextFieldIsFocused)
.onSubmit {
if !renamingTextFieldUserInput.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
titles[index] = renamingTextFieldUserInput
}
}
.onDisappear {
renamingTextFieldIsFocused = false
}
.onChange(of: renamingTextFieldIsFocused) { isFocused in
if !isFocused {
renamingTitleIndex = nil
renamingTextFieldUserInput = ""
}
}
}
}
}
}
}
Is there a way to get the FocusState
property to work so I don't have manually tap the TextField
when this happens? I know I could disable all the other rows while a row is editing (I probably will end up doing this regardless), but I'd also like to see if I can find a solution to this problem as I have other NavigationLinks
in other Lists
that could be triggered and would like to avoid having to disable all of them when a TitleList
row is editing.
Add a delay when setting focus, this ensures the focus is set after the view is fully rendered.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
renamingTextFieldIsFocused = true
}