简体   繁体   中英

How do I connect SwiftUI to a Document in a macOS Document-based app?

I'm trying to create a basic Document-Based SwiftUI app. However, the skeleton created by Xcode does not have any linkage between the document and the content view. Here is what I have inferred to be the correct way:

struct Task {
  var done: Bool
  var text: String
}

class Document: NSDocument {
  var task: Task = Task(done: false, text: "") // Declaration A: task content
  override class var autosavesInPlace: Bool { true }
  override func makeWindowControllers() {
    let contentView = ContentView(document: self)
    let window = NSWindow(
      contentRect: NSRect(x: 0, y: 0, width: 250, height: 300),
      styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
      backing: .buffered, defer: false)
    window.center()
    window.contentView = NSHostingView(rootView: contentView)
    let windowController = NSWindowController(window: window)
    self.addWindowController(windowController)
  }
}

struct ContentView: View {
  @State var document: Document // Declaration B: link to document
  var body: some View {
    HStack {
      Toggle(isOn: $document.task.done) { EmptyView() }
      TextField("Description", text: $document.task.text)
    }.padding()
  }
}

(This is the entirety of my code, aside from Xcode's generated skeleton)

However, in the resulting app, edits do not appear to have any effect. The checkbox is not checkable, and edits to the text field are reverted.

What is the correct way to declare the document's content, and the link between the document and content view, if not declarations A and B above?

Here's the best I have so far:

class Document: NSDocument, ObservableObject {
  @Published var task: Task = Task(done: false, text: "") {
    willSet {
      let copy = task
      undoManager?.registerUndo(withTarget: self) { $0.task = copy }
    }
  }

  // makeWindowControllers() unchanged from above
}

struct ContentView: View {
  @ObservedObject var document: Document
  var body: some View {
    HStack {
      Toggle(isOn: $document.task.done) { EmptyView() }
      TextField("Description", text: $document.task.text)
    }.padding()
  }
}

Undo now works, but in the case of the text field, it operates on one character at a time. I don't know how to support normal text-field undo while also allowing document-level undo. I think the text-field should have its own, separate undoManager, but I don't see a hook for that in SwiftUI.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM