我曾尝试使用 Coordinator 类创建一个 TextView 但这似乎总是保留与原始对象的绑定。
如果我在这样的 SwiftUI 视图中使用它,那么如果我在主/详细信息排列中使用 DocView 并传入一个新的 Doc 对象,那么 TextView 将始终保持与第一个文档的绑定。
因此,如果有文档或项目列表,则选择第二个或第三个文档并为该文档创建DOCVIEW时,对文本视图的任何编辑都将更新第一个文档详细信息。
因此,似乎协调器或其他东西保留了原始绑定,而不是更新以反映 DocView 变量 doc 的最新实例。
我是否误解了 Coordinator 应该如何工作,如果是这样,我如何确保在创建新的 DocView(doc: newdocobject) 时更新绑定。
实际上,TextView 将显示 newdocobject 详细信息,但任何时候进行任何编辑,目标似乎仍然是原始对象
编辑:示例应用程序https://duncangroenewald.com/files/SampleApps/TextView.zip
问题的更好说明 https://duncangroenewald.com/files/SampleApps/TextView2.zip
struct DocView: View {
@ObservedObject var doc: Doc
var body: some View {
TextView(attributedText: $doc.details)
}
}
class Doc: NSObject, ObservableObject {
var name: String
var details: NSAttributedString
init(name: String) {
self.name = name
self.details = NSAttributedString(string:"New doc details")
}
}
//
// TextView.swift
// DocApp
//
// Created by Duncan Groenewald on 11/12/21.
// Copyright © 2021 Apple. All rights reserved.
//
import Foundation
import SwiftUI
#if !os(macOS)
struct TextView: UIViewRepresentable {
@Binding var attributedText: NSAttributedString
func makeUIView(context: Context) -> OSTextView {
let textView = OSTextView()
textView.delegate = context.coordinator
return textView
}
func updateUIView(_ uiView: OSTextView, context: Context) {
uiView.attributedText = attributedText
}
func makeCoordinator() -> Coordinator {
Coordinator($attributedText)
}
class Coordinator: NSObject, UITextViewDelegate {
var text: Binding<NSAttributedString>
init(_ text: Binding<NSAttributedString>) {
self.text = text
}
func textViewDidChange(_ textView: UITextView) {
self.text.wrappedValue = textView.attributedText
}
}
}
#endif
#if os(macOS)
struct TextView: NSViewRepresentable {
@Binding var attributedText: NSAttributedString
func makeNSView(context: Context) -> OSTextView {
let textView = OSTextView(frame: .zero)
textView.delegate = context.coordinator
return textView
}
func updateNSView(_ nsView: OSTextView, context: Context) {
nsView.textStorage?.setAttributedString(attributedText)
}
func makeCoordinator() -> Coordinator {
return Coordinator($attributedText)
}
class Coordinator: NSObject, NSTextViewDelegate {
var text: Binding<NSAttributedString>
init(_ text: Binding<NSAttributedString>) {
self.text = text
super.init()
}
func textDidChange(_ notification: Notification) {
if let textView = notification.object as? NSTextView {
self.text.wrappedValue = textView.attributedString()
}
}
}
}
#endif
编辑:我认为 Apples 实现 NSAttributedString 绑定可能存在错误。
通过执行以下操作,我似乎已经克服了这个问题。
TextView(attributedText: Binding(get: {doc.details}, set: {doc.details = NSAttributedString(attributedString: $0)}))
没有比这更糟糕的了——SwiftUI 似乎对它的绑定完全感到困惑,并保留了对前一个对象的引用。
我的解决方法是在全局变量(单例)中保留对视图中显示的选定对象的引用,并对单例中保存的对象执行 get{} set{}。
这现在似乎工作只是找到。我已经向 Apple 记录了错误报告。不确定这是否与 NSAttributedString 或是否与 NSViewRepresentable 有关。
所以绑定现在看起来像这样
TextView(attributedText: Binding(get: {
return get()
}, set: {
set($0)
}))
而这两种方法是这样的。请注意,我仍然必须复制属性字符串!
func get()->NSAttributedString {
if let no = fileList.selectedNode {
return no.wrappedDetails
} else {
return NSAttributedString(string: "")
}
} func set(_ attribString: NSAttributedString) {
if let no = fileList.selectedNode {
no.wrappedDetails = NSAttributedString(attributedString: attribString)
}
}