You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 06:05:18