我正在尝试在Swift 2中创建一个绘图应用程序,并且在尝试实现“撤消”按钮时撞墙。我对此非常陌生(实际上是非常新的),并且我已尽力解决这个问题,但我发现的所有其他示例都是使用不同的版本或语言,或者只是不适用..也许正是我缺乏知识阻碍了我在这里的进步。
我尝试过使用'undoManager',但我不知道如何使用它。我曾尝试阅读可用的在线指南,但仍然无能为力!
这是我的按钮:
@IBAction func undoDrawing(sender: AnyObject) {
}
我已设置touchesBegan
:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?){
isSwiping = false
if let touch = touches.first{
lastPoint = touch.locationInView(imageView)
}
}
touchesMoved
:
override func touchesMoved(touches: Set<UITouch>,
withEvent event: UIEvent?){
isSwiping = true;
if let touch = touches.first{
let currentPoint = touch.locationInView(imageView)
UIGraphicsBeginImageContext(self.imageView.frame.size)
self.imageView.image?.drawInRect(CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height))
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y)
CGContextSetLineCap(UIGraphicsGetCurrentContext(),CGLineCap.Round)
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), myCGFloat)
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), self.selectedColor.CGColor)
CGContextStrokePath(UIGraphicsGetCurrentContext())
self.imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
lastPoint = currentPoint
}
}
和touchesEnded
:
override func touchesEnded(touches: Set<UITouch>,
withEvent event: UIEvent?){
if(!isSwiping) {
UIGraphicsBeginImageContext(self.imageView.frame.size)
self.imageView.image?.drawInRect(CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height))
CGContextSetLineCap(UIGraphicsGetCurrentContext(), CGLineCap.Round)
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), myCGFloat)
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), self.selectedColor.CGColor)
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
CGContextStrokePath(UIGraphicsGetCurrentContext())
self.imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
}
所以我只需要一种方法来扭转这些'触动'所采取的行动。有什么想法吗?
答案 0 :(得分:4)
我可以想到两种方法,轻松和放松。有限的方式和艰难的概括的方式。
最简单的方法是只允许撤消单个更改,即在内存中维护图像的第二个副本。在伪代码中,
// Update contents of backupImage, draw new line on currentImage
drawNewLine {
backupImage = currentImage
drawLineOnCurrentImage()
}
// Revert currentImage to most-recent backupImage
undo {
currentImage = backupImage
}
这类似于方案used by the original MacPaint(参见“问题3:你如何撤消?”)。
此方法不允许重做,也不需要(或允许)使用NSUndoManager
。
对于更通用的解决方案,您需要弄清楚如何将每个绘图事件表示为可逆函数。
原始示例应用
想象一下,绘图应用程序支持的唯一操作是缩放。因此,
scale(image, 2.0) // push 2.0 onto stack - current size = 2.0x
scale(image, 3.0) // push 3.0 onto stack - current size = 6.0x
返回的尺寸是图像的六倍。您可以使用一堆浮点表示此操作,例如scaleHistory = [2.0, 3.0]
。
然后,您的撤消功能将是
undo(image) {
scale(image, 1 / scaleHistory.lastObject())
scaleHistory.removeLastObject()
}
从上面继续执行,
undo(image) // Pop 3.0 from the stack. Scale by 1 / 3.0, new size = 2.0x
undo(image) // Pop 2.0 from the stack. Scale by 1 / 2.0, new size = 1.0x
现在scaleHistory
为空,您应该停用undo
按钮!
这是NSUndoManager
所期望的范式;请参阅this article,其中介绍了撤消堆栈的使用。
如何在您的应用中发挥作用
我看到你实现这个范例的一种方法是直接替换图像上的绘图,并在数据结构中记录绘图操作的意图。因此,您将有一个CGPoint
对向量,表示用户添加的行。在伪代码中,
lines = []
addLine(start, finish) {
lines.addObject((start, finish))
draw()
}
draw() {
canvas = nil
foreach (lines as line) {
canvas.paintToScreen(line) // this function invokes CGContextMoveToPoint, CGContextAddLineToPoint, etc.
}
}
undo() {
lines.removeLastObject()
draw()
}
canUndo() {
return lines.count > 0
}
至关重要的是,您必须在每次调用canvas
时重置draw()
,以便确保lines
中的每一行都只呈现一次。
当然,这与缩放应用程序并不完全相似,因为undo()
并不是简单地应用最后一个操作的反转。相反,我们重置屏幕上的图像 - 相当于撤消所有操作 - 然后选择性地重新执行我们想要保留的 draw()
函数