NavigationStack跳转后FocusState失效问题求助
问题描述
我有一个TitleList视图,用户可通过.swipeAction将NavigationLink替换为TextField来编辑行标题。当前存在的问题是:当正在编辑标题时,若用户点击其他行的NavigationLink并通过根视图的NavigationStack跳转至新视图,返回根视图后再次尝试编辑该行,尽管已将其FocusState属性设为true,TextField仍无法自动激活(需手动点击才弹出键盘)。
原代码
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 = "" } } } } } } }
问题根源
跳转返回后焦点设置失效的核心原因是:当从子视图返回时,SwiftUI需要重新构建编辑状态的TextField视图,而原代码中先设置renamingTextFieldIsFocused = true再切换renamingTitleIndex,此时TextField还未完成渲染,焦点设置无法绑定到实际的视图元素上。另外,onDisappear中的无条件焦点重置逻辑,会在页面跳转时干扰后续的焦点恢复。
解决方案
通过调整状态更新顺序、优化焦点重置逻辑即可解决问题,修改后的代码如下:
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 { // 先切换视图状态,确保TextField开始渲染 renamingTitleIndex = index // 预填充原标题,提升编辑体验 renamingTextFieldUserInput = titles[index] // 延迟设置焦点,等待TextField完成视图构建 DispatchQueue.main.async { renamingTextFieldIsFocused = true } } label: { Label("Rename", systemImage: "pencil") } } } else { VStack { TextField("", text: $renamingTextFieldUserInput) .focused($renamingTextFieldIsFocused) .onSubmit { let trimmedInput = renamingTextFieldUserInput.trimmingCharacters(in: .whitespacesAndNewlines) if !trimmedInput.isEmpty { titles[index] = trimmedInput } // 提交后主动重置所有状态 renamingTitleIndex = nil renamingTextFieldUserInput = "" renamingTextFieldIsFocused = false } .onChange(of: renamingTextFieldIsFocused) { isFocused in if !isFocused { // 仅当当前行处于编辑状态时重置,避免跳转时被动触发 if renamingTitleIndex == index { renamingTitleIndex = nil renamingTextFieldUserInput = "" } } } } } } } }
关键修改点
- 调整状态更新顺序:先设置
renamingTitleIndex让TextField视图进入渲染流程,再通过DispatchQueue.main.async延迟设置焦点,确保焦点绑定到已渲染完成的TextField上。 - 优化预填充逻辑:点击重命名时自动填入原标题,无需用户重新输入。
- 移除不必要的
onDisappear逻辑:避免页面跳转时被动触发焦点重置,仅在主动失去焦点且当前行处于编辑状态时才重置状态。 - 提交逻辑闭环:提交编辑后主动重置所有相关状态,确保视图回到正常状态。
内容的提问来源于stack exchange,提问作者Ser Pounce
相关产品推荐
相关产品推荐

