排序后NSViewRepresentable中的数组绑定更新异常问题
解决SwiftUI排序后NSViewRepresentable绑定更新异常问题
在SwiftUI场景中,将ForEach绑定到包含index和text字段的ListItem数组,通过NSViewRepresentable封装NSTextView并绑定到对应item的text属性时,执行排序操作后会出现异常:更新某个NSTextView时,其他视图会同步错误更新。
问题根源
- 视图复用导致绑定错位:SwiftUI的
ForEach会复用已有视图实例以优化性能。排序后,NSTextView实例被复用,但原代码中协调器持有旧的TVRep值类型拷贝,其内部绑定仍指向排序前的ListItem,导致编辑时错误更新旧数据源。 - 无条件覆盖用户输入:原
updateNSView方法无条件设置nsView.string = text,SwiftUI状态更新时会覆盖用户正在输入的内容,加剧显示混乱。
修复方案
修改TVRep的实现,让协调器直接持有绑定引用,并在视图更新时同步绑定,同时避免无条件覆盖输入:
struct TVRep: NSViewRepresentable { @Binding var text: String func makeNSView(context: Context) -> NSTextView { let view = NSTextView() view.string = text view.delegate = context.coordinator return view } func updateNSView(_ nsView: NSTextView, context: Context) { // 仅当内容不一致时更新,避免干扰用户编辑 if nsView.string != text { nsView.string = text } // 更新协调器的绑定,确保指向当前正确的text属性 context.coordinator.textBinding = text } func makeCoordinator() -> Coordinator { Coordinator(textBinding: text) } class Coordinator: NSObject, NSTextViewDelegate { var textBinding: Binding<String> init(textBinding: Binding<String>) { self.textBinding = textBinding super.init() } func textDidChange(_ notification: Notification) { guard let tv = notification.object as? NSTextView else {return} textBinding.wrappedValue = tv.string } } }
关键改进点
- 协调器直接持有绑定:让协调器保存
Binding<String>引用,而非依赖TVRep实例,确保始终操作当前正确的数据源。 - 动态更新绑定:在
updateNSView中同步协调器的绑定引用,适配视图复用后的新绑定关系。 - 条件性更新视图内容:仅当
NSTextView内容与绑定值不一致时才更新,避免覆盖用户正在输入的内容。
内容的提问来源于stack exchange,提问作者Michael Berk
相关产品推荐
相关产品推荐

