为什么@Environment
UndoManager
在其堆栈中有操作时不更新其canUndo
属性?我有一个视图,它有一个子视图,可以使用un/redo功能,但由于某种原因,我无法禁用基于管理器的undo按钮。
struct MyView: View {
@Environment(.undoManager) var undoManager: UndoManager?
var body: some View {
Button("Undo") { ... }
.disabled(!self.undoManager!.canUndo)
}
}
UndoManager.canUndo
不符合KVO,因此使用一些通知发布程序来跟踪状态,如下面的
struct MyView: View {
@Environment(.undoManager) var undoManager
@State private var canUndo = false
// consider also other similar notifications
private let undoObserver = NotificationCenter.default.publisher(for: .NSUndoManagerDidCloseUndoGroup)
var body: some View {
Button("Undo") { }
.disabled(!canUndo)
.onReceive(undoObserver) { _ in
self.canUndo = self.undoManager!.canUndo
}
}
}
当谈到canRedo
时,我尝试了多种方法,结果是这样的——所以观察viewModel
(或document
或任何其他支持撤消的数据源(并根据其变化更新canUndo
/canRedo
:
struct MyView: View {
@ObservedObject var viewModel: ViewModel
@Environment(.undoManager) private var undoManger: UndoManager!
@State private var canUndo = false
@State private var canRedo = false
var body: some View {
RootView()
.onReceive(viewModel.objectWillChange) { _ in
canUndo = undoManger.canUndo
canRedo = undoManger.canRedo
}
if canUndo {
Button(
action: { undoManger?.undo() },
label: { Text("Undo") }
)
}
if canRedo {
Button(
action: { undoManger?.redo() },
label: { Text("Redo") }
)
}
...
我还将其封装在一个独立的按钮中(没有将实现过度概括到我自己的需求之上(,它从我的视图中消除了样板,并使复杂性更加私密,因此对我来说最终是这样的:
struct MyView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
RootView()
UndoManagerActionButton(
.undo,
willChangePublisher: viewModel.objectWillChange
)
UndoManagerActionButton(
.redo,
willChangePublisher: viewModel.objectWillChange
)
...