SwiftUI 绑定在项目重新排序后编辑错误的 TextField
SwiftUI Binding Edits Wrong TextField after Item Reordered
Xcode 13 测试版 5,iOS 14,macOS 11.6
我有一个 parent SwiftUI 视图,其中列出了一些 children。每个 child 都绑定到一个 NSViewRepresentable
。一切正常,我可以按预期编辑值。但是一旦我重新排序列表中的项目并编辑一个字段,它编辑了错误的字段。 之前的商品订单。
的绑定似乎完好无损
这是它的样子:
这是parent:
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)
}
}
...这是 child 视图:
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下载并试用。
我认为问题(错误?)与 ForEach
生成的绑定有关。
如果您放弃生成的绑定并创建自己的绑定,一切似乎都按预期工作。
添加到 ThingModel
:
func bindingForThing(id: String) -> Binding<Thing> {
.init {
self.things.first { [=10=].id == id }!
} set: { newThing in
self.things = self.things.map { [=10=].id == id ? newThing : [=10=] }
}
}
和 ParentView
:
ForEach(model.things){ thing in
ChildView(thing: model.bindingForThing(id: thing.id))
Xcode 13 测试版 5,iOS 14,macOS 11.6
我有一个 parent SwiftUI 视图,其中列出了一些 children。每个 child 都绑定到一个 NSViewRepresentable
。一切正常,我可以按预期编辑值。但是一旦我重新排序列表中的项目并编辑一个字段,它编辑了错误的字段。 之前的商品订单。
这是它的样子:
这是parent:
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)
}
}
...这是 child 视图:
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下载并试用。
我认为问题(错误?)与 ForEach
生成的绑定有关。
如果您放弃生成的绑定并创建自己的绑定,一切似乎都按预期工作。
添加到 ThingModel
:
func bindingForThing(id: String) -> Binding<Thing> {
.init {
self.things.first { [=10=].id == id }!
} set: { newThing in
self.things = self.things.map { [=10=].id == id ? newThing : [=10=] }
}
}
和 ParentView
:
ForEach(model.things){ thing in
ChildView(thing: model.bindingForThing(id: thing.id))