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

SwiftUI列表重排序后绑定NSViewRepresentable的TextField编辑错误问题

问题原因

  1. Coordinator 持有了初始化时的旧 GrowingField 实例
    你在 Coordinator 中用 let parent: GrowingField 存储了初始化时传入的实例,这个值一旦生成就不会再更新。列表重排序后,同位置的 GrowingField 的 text 绑定、submit 闭包已经指向了新的列表项,但 Coordinator 里的 parent 还是初始化时的旧实例,所以触发回调时会对应到重排序前的逻辑,造成绑定错位。
  2. 视图复用导致状态残留
    默认情况下 ForEach 会复用同位置的视图实例,重排序后同位置的 NSTextField 不会重新创建,只会调用 updateNSView 刷新,你之前的代码只更新了 textBinding,没有同步最新的 submit 回调,就会出现状态残留。

修复方案

第一步:修改 GrowingField 逻辑,移除 Coordinator 对旧实例的持有

不要在 Coordinator 中存储固定的 GrowingField 实例,每次调用 updateNSView 时同步最新的绑定和回调:

struct GrowingField: NSViewRepresentable{
  @Binding var text: String
  var submit:(() -> Void)? //Hit enter
  
  func makeNSView(context: Context) -> NSTextField {
    let textField = NSTextField()
    textField.delegate = context.coordinator
    textField.stringValue = text
    return textField
  }
  func updateNSView(_ nsView: NSTextField, context: Context) {
    nsView.stringValue = text
    // 同步最新的绑定和回调
    context.coordinator.textBinding = $text
    context.coordinator.currentSubmit = submit
  }

  func makeCoordinator() -> Coordinator {
    Coordinator()
  }
  
  class Coordinator: NSObject, NSTextFieldDelegate {
    var textBinding : Binding<String>?
    var currentSubmit: (() -> Void)?
    
    func controlTextDidChange(_ obj: Notification) {
      guard let textField = obj.object as? NSTextField else { return }
      self.textBinding?.wrappedValue = textField.stringValue
    }
    
    func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
      switch commandSelector{
        case #selector(NSStandardKeyBindingResponding.insertNewline(_:)):
        currentSubmit?()
        textView.window?.makeFirstResponder(nil)
        return true
      default:
        return false
      }
    }
  }
}

第二步:给列表项添加唯一标识(可选优化)

给 ChildView 加上基于数据 ID 的标识,确保重排序后数据变化时会重新创建视图,避免不必要的复用带来的状态问题:

ForEach($model.things){ $thing in
  ChildView(thing: $thing)
    .id(thing.id)
    .onDrag{
      model.draggedThing = thing
      return NSItemProvider(object: NSString())
    }
}

内容的提问来源于stack exchange,提问作者Clifton Labrum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 22:48:03