【问题标题】:SwiftUI Binding Edits Wrong TextField after Item Reordered项目重新排序后,SwiftUI 绑定编辑错误的文本字段
【发布时间】:2021-11-30 17:23:55
【问题描述】:

Xcode 13 beta 5、iOS 14、macOS 11.6

我有一个父级 SwiftUI 视图,其中列出了一些子级。每个孩子都绑定到NSViewRepresentable。一切正常,我可以按预期编辑值。但是,一旦我对列表中的项目重新排序并编辑了一个字段,它编辑了错误的字段。从之前的项目订单看来,绑定仍然完好无损。

看起来是这样的:

这是父级:

struct ParentView: View {
  @StateObject var model = ThingModel.shared
  
  var body: some View {
    VStack{
      ForEach($model.things){ $thing in
        ChildView(thing: $thing)
        //Reorder
        .onDrag{
          model.draggedThing = thing
          return NSItemProvider(object: NSString())
        }
      }
      Text("Value: \(model.value)").font(.title)
    }
    .frame(width:300, height: 200)
  }
}

...这是子视图:

struct ChildView: View {
  @Binding var thing: Thing
  @StateObject var model = ThingModel.shared
  
  var body: some View{
    HStack{
      GrowingField(text: $thing.text, submit: {
        model.value = thing.text
        print(thing.text)
      })
      Text(" = ")
        .opacity(0.4)
    }
    .padding(10)
    .onDrop(of: [UTType.text], delegate: ThingReorderDelegate(hoveredThing: thing))
  }
}

最后,这里是NSViewRepresentable,称为GrowingField。为简单起见,我省略了NSTextField 子类。

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
  }

  //Delegates
  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }
  
  class Coordinator: NSObject, NSTextFieldDelegate {
    let parent: GrowingField
    var textBinding : Binding<String>?
    
    init(_ field: GrowingField) {
      self.parent = field
    }
    
    func controlTextDidChange(_ obj: Notification) {
      guard let textField = obj.object as? NSTextField else { return }
      self.textBinding?.wrappedValue = textField.stringValue
    }
    
    //Listen for certain keyboard keys
    func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
      switch commandSelector{
        case #selector(NSStandardKeyBindingResponding.insertNewline(_:)):
        //- Enter -
        parent.submit?()
        textView.window?.makeFirstResponder(nil) //Blur cursor
        return true
      default:
        return false
      }
    }
    
  }
}

为什么对NSViewRepresentable的绑定在重新排序后不跟随字段?

Here is a sample project 下载并试用。

【问题讨论】:

    标签: swift cocoa swiftui nsviewrepresentable


    【解决方案1】:

    我认为问题(错误?)与 ForEach 生成的绑定有关。

    如果您放弃生成的绑定并创建自己的绑定,一切似乎都按预期工作。

    添加到ThingModel

    func bindingForThing(id: String) -> Binding<Thing> {
        .init {
            self.things.first { $0.id == id }!
        } set: { newThing in
            self.things = self.things.map { $0.id == id ? newThing : $0 }
        }
    }
    

    还有ParentView

    ForEach(model.things){ thing in
        ChildView(thing: model.bindingForThing(id: thing.id))
    

    【讨论】:

    • 嗯...有趣!谢谢你。我一直在寻找我的NSViewRepresentable 的问题,因为如果我将ChildView 内部的GrowingField 替换为普通的库存TextView(),那么绑定就可以正常工作。 ?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 2021-10-09
    • 1970-01-01
    相关资源
    最近更新 更多