SwiftUI中ForEach嵌套UITextView拖拽后文本丢失问题求助
问题原因
你当前用数组索引作为ForEach的id,拖拽排序时SwiftUI会根据索引重用视图。原绑定旧索引的TestEditor会被分配给新数组元素,但TestEditor里的@State var i不会自动更新,且makeUIView仅在视图初始化时设置一次文本,导致拖拽后编辑器显示旧元素文本,看起来像是丢失输入内容。
解决方案
1. 给Blocks模型添加唯一标识
让Blocks遵循Identifiable协议,确保每个元素有唯一标识,让ForEach能正确跟踪元素:
struct Blocks: Identifiable { let id = UUID() var text: String }
2. 修改ContentView的遍历逻辑
直接遍历数组元素,用绑定传递单个元素,替代索引遍历:
struct ContentView: View { @StateObject var myList = ListBlocks() func move(from source: IndexSet, to destination: Int) { myList.list.move(fromOffsets: source, toOffset: destination) } var body: some View { List { ForEach($myList.list) { $block in WrapTest(block: $block) } .onMove(perform: move) } } }
3. 更新WrapTest视图
接收单个元素的绑定,不再传递整个ListBlocks和索引:
struct WrapTest: View { @Binding var block: Blocks var body: some View { TestEditor(block: $block) } }
4. 重构TestEditor
直接用元素绑定同步文本,移除索引依赖,优化UITextView更新逻辑:
struct TestEditor: UIViewRepresentable { @Binding var block: Blocks func makeUIView(context: Context) -> UITextView { let textView = UITextView() textView.delegate = context.coordinator textView.text = block.text return textView } func updateUIView(_ uiView: UITextView, context: Context) { // 仅当文本不一致时更新,避免输入时被视图更新覆盖内容 if uiView.text != block.text { uiView.text = block.text } } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextViewDelegate { var parent: TestEditor init(_ parent: TestEditor) { self.parent = parent } func textViewDidChange(_ textView: UITextView) { // 实时同步输入文本到绑定的元素 parent.block.text = textView.text } } }
核心改进说明
- 用
Identifiable元素的唯一id替代索引作为ForEach标识,确保拖拽后视图与元素正确关联,避免错误重用。 - 通过
@Binding直接绑定单个Blocks元素,彻底消除索引变化带来的同步问题。 - 在
updateUIView中添加文本判断,防止SwiftUI视图更新干扰用户输入。
内容的提问来源于stack exchange,提问作者Stefano Vet
相关产品推荐
相关产品推荐

