从SwiftUI环境捕获UndoManager
我希望能够从文档模型内访问UndoManager,以便可以从模型内注册撤消操作:从SwiftUI环境捕获UndoManager,swiftui,Swiftui,我希望能够从文档模型内访问UndoManager,以便可以从模型内注册撤消操作: // Assume I've extended MyDocument to conform to ReferenceFileDocument elsewhere... final class MyDocument { private var undoManager: UndoManager? @Published var aNumber = 5 { willSet {
// Assume I've extended MyDocument to conform to ReferenceFileDocument elsewhere...
final class MyDocument {
private var undoManager: UndoManager?
@Published var aNumber = 5 {
willSet {
if let undoManager = undoManager {
let currentValue = self.aNumber
undoManager.registerUndo(withTarget: self) { target in
target.aNumber = currentValue
}
}
}
}
func setUndoManager(undoManager: UndoManager?) {
self.undoManager = undoManager
}
}
为了注册undoManager,我尝试了以下方法:
struct DocumentView: View {
let document : MyDocument
@Environment(\.undoManager) var undoManager
var body: some View {
MyDocumentEditor(document: document)
.onAppear {
document.setUndoManager(undoManager: undoManager)
}
}
}
运行我的应用程序并加载已保存的文档时,此操作有效。但当从新文档开始时,UndoManager为零
我试过这样的方法:
@Environment(\.undoManager) var undoManager {
didSet {
self.document.setUndoManager(undoManager: undoManager)
}
}
我的目标是尽可能地在模型和视图中保持尽可能多的逻辑,只关注UI内容。我希望ReferenceFileDocument提供一个属性来访问其关联的UndoManager,就像NSDocument提供的那样。我已经找到了一个解决方案-尽管它感觉不太正确。在视图的顶层,我将undoManager传递给我在文档上保留的属性:
struct ContentView: View {
let document: MyDocument
@Environment(\.undoManager) var undoManager
var body: some View {
document.setUndoManager(undoManager: undoManager)
return TopLevelView(document: document)
}
}
SwiftUI使用以下方法看起来更自然
var body: some View {
TopLevelView(document: document, undoManager: undoManager)
}
及
经过一天多的努力,我的收获是
环境中的UndoManager
是连接到视图所在的NSWindow
的。我的解决办法是:
protocol Undoable {
func inverted() -> Self
}
class Store<State, Action : Undoable> {
var state : State
var reducer : (inout State, Action) -> Void
//...init...
func send(_ action: Action, undoManager: UndoManager) {//passed as an argument
reducer(&state, action)
undoManager.registerUndo(withTarget: self){target in
target.send(action.inverted())
}
}
//...other methods...
}
(也许您需要将调度程序
放入状态对象
,我没有测试该部分,因为我很高兴在我的小应用程序中将撤销管理器作为函数参数传递给您)。是的-感觉更好。但是,UndoManager不符合ObserveObject,因此其属性定义需要省略@ObserveObject
,因此为var UndoManager:UndoManager?
protocol Undoable {
func inverted() -> Self
}
class Store<State, Action : Undoable> {
var state : State
var reducer : (inout State, Action) -> Void
//...init...
func send(_ action: Action, undoManager: UndoManager) {//passed as an argument
reducer(&state, action)
undoManager.registerUndo(withTarget: self){target in
target.send(action.inverted())
}
}
//...other methods...
}
class Dispatcher<State, Action : Undoable> : ObservableObject {
let store : Store<State, Action>
let undoManager : UndoManager //see below
//...init...
func send(_ action: Action) {
objectWillChange.send()
store.send(action, undoManager: undoManager)
}
}
struct ContentView<State, Action : Undoable> : View {
@Environment(\.undoManager) var undoManager
let document : Store<State, Action>
var body : some View {
ViewHierarchy().environmentObject(Dispatcher(store: document,
undoManager: undoManager)
}
}