SwiftUI Todo应用:TextField输入单个字符后失去焦点问题求助
问题原因与解决方案
问题根源
你遇到的TextField输入时失去焦点的问题,核心原因是**ForEach使用了id: \.self作为元素标识**。由于Entry遵循Hashable协议,当修改TextField中的task内容时,整个Entry实例的哈希值会发生变化,ForEach会将其判定为全新元素,进而销毁原有的EntryView并重新创建视图——这就导致正在编辑的TextField被重建,焦点自然丢失。
修复方案
只需修改ForEach的标识规则,改用Entry自带的id(Entry已实现Identifiable协议,自带唯一的UUID类型id)。这样即使Entry内容变化,标识id始终不变,ForEach只会更新现有视图的内容,不会重建视图,焦点就能稳定保持。
修改后的List部分代码:
List{ // 利用Identifiable的默认id,无需指定id: \.self ForEach($tasks){ i in EntryView(entry: i) } }
也可以显式指定id: \.id,效果完全一致:
List{ ForEach($tasks, id: \.id){ i in EntryView(entry: i) } }
关于延迟渲染的补充
你提到的“延迟到提交编辑或按Enter再渲染”其实没必要,上述修改已经能完美解决焦点问题,同时保持实时编辑的体验。如果确实需要实现“编辑完成后再同步到数据源”的逻辑,可以在EntryView中新增临时@State变量缓存输入内容,监听TextField的onCommit事件(Mac上按Enter触发)再同步到绑定的entry,示例代码如下:
struct EntryView: View { @Binding var entry: Entry @State private var tempTask: String init(entry: Binding<Entry>) { self._entry = entry self._tempTask = State(initialValue: entry.wrappedValue.task) } var body: some View { HStack { Toggle("Done", isOn: $entry.checked).labelsHidden() Divider() Spacer() TextField("Enter:", text: $tempTask) .onCommit { entry.task = tempTask } } } }
但这种方案会增加复杂度,优先推荐第一种直接修改ForEach标识的方案。
内容的提问来源于stack exchange,提问作者Sami Ben
相关产品推荐
相关产品推荐

