【发布时间】:2016-09-26 23:07:06
【问题描述】:
我正在开发一个项目,该项目包括一个注释工具,允许用户用手指或铅笔在文档上“绘图”。自然,我热衷于为绘制的路径实现撤消/重做。
我的绘图应用程序的实现相对传统。用户在屏幕上看到的是缓存的位图图像(在当前路径之前绘制的所有路径的快照)与当前路径的“实时”渲染(UIBezierPath)的组合。当 touchesEnded 被触发时,新的路径被添加到位图中。
我已经能够以相对较少的麻烦实现撤消。我为该类创建了一个标准的 undoManager:
let myUndoManager : UndoManager = {
let mUM : UndoManager = UndoManager()
mUM.levelsOfUndo = 6
return mUM
}()
在 touchesEnded 结束时调用的用于渲染新缓存路径的函数称为 drawBitmap。在这个函数开始时,假设有一个以前的缓存路径,在绘制新路径之前,我向 undo manager 注册了以下 undo action:
let previousCachedPath : UIImage = self.cachedPath
self.myUndoManager.registerUndo(withTarget: self, selector: #selector(self.setBitmap(_:)), object: previousCachedPath)
setBitmap(_ previousCachedPath : UIImage) 是将显示的位图重置为提供的图像的函数。
我有一个分别与 undo() 和 redo() 方法相关联的撤消/重做按钮。除了一些逻辑指示这些按钮何时应该处于活动状态(即确保在没有绘制任何内容时不能按下撤消等),这些只是分别调用 myUndoManager.undo() 和 myUndoManager.redo():
func undo() -> Void {
guard self.myUndoManager.canUndo else { return }
self.myUndoManager.undo()
if !self.redoButton.isEnabled {
self.redoButton.isEnabled = true
}
if !self.myUndoManager.canUndo {
self.undoButton.isEnabled = false
}
self.setNeedsDisplay()
}
func redo() -> Void {
guard self.myUndoManager.canRedo else { return }
self.myUndoManager.redo()
if !self.undoButton.isEnabled {
self.undoButton.isEnabled = true
}
if !self.myUndoManager.canRedo {
self.redoButton.isEnabled = false
}
self.setNeedsDisplay()
}
正如我所提到的,撤消对指定的六个可撤消级别非常有效。但是,我显然缺少重做的东西。我最初的希望是 undoManager 会在调用 undo 时自动将 undo 任务从 undo 堆栈转移到 redo 堆栈,但这显然不会发生。
我已经搜索过答案,我认为最接近我需要的可能是按照以下方式使用相互递归:
Using NSUndoManager, how to register undos using Swift closures
但是,我无法完成这项工作。因此,任何帮助表示赞赏!
【问题讨论】:
-
诀窍是你应该只有 一个 方法,撤消和重做都应该调用它。重做的撤消是撤消。撤消的撤消是重做。它可能会帮助你阅读我的书章节:apeth.com/iOSBook/ch39.html
-
谢谢 - 今天早上我会读一读,让你知道我的进展如何!
-
@matt 非常感谢,工作完美。正如您所建议的,我将所有内容组合成一种方法,在本例中为 setBitmap(_:)。我让它与您概述的任何一种方法一起工作。我已经发布了我的解决方案,以防它帮助其他人。
标签: ios swift nsundomanager