首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在存储UIGraphicsContext的iOS中执行undo操作?

如何在存储UIGraphicsContext的iOS中执行undo操作?
EN

Stack Overflow用户
提问于 2013-02-01 18:55:35
回答 4查看 2.1K关注 0票数 5

我想在我的绘图应用程序中对图形上下文执行撤消操作。当按下undo时,我想要移动到包含旧绘图的前一个上下文。

例如:

我在上下文中有一个矩形。拖动时,我将矩形移动到新位置并重绘它。现在,当我按下undo按钮时,我想要将矩形移动到以前的位置。我该怎么做呢?

我对NSUndoManager只有基本的了解。

请帮帮我!

谢谢。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-02-06 11:35:59

你所推断的功能实际上并不存在。换句话说,"Graphics contexts不会以这种方式(免费)工作。“您可能见过函数UIGraphicsPushContextUIGraphicsPopContext,但它们并不能实现您在这里所说的功能。它们所代表的“推”和“弹出”操作操作于图形上下文的尚未渲染的方面:例如,当前填充和笔划颜色、裁剪矩形和变换矩阵。一旦某些东西被渲染到上下文中--例如,路径/rect/等被描边或填充,或者图像被合成到上下文中--它将永远被渲染。“返回”的唯一方法是以某种方式重新创建以前的状态。不幸的是,UIGraphicsContext没有什么神奇的内置功能可以帮你实现这一点。

有几种方法可以做到这一点。正如另一位用户所提到的,您可以在每次用户操作之后捕获上下文的状态。简而言之,这意味着您的文档“模型”是一个位图堆栈。这很快就会占用大量内存--位图占用大量内存。当然,你可以对这种方法进行优化,以获得更多的里程,比如只保存每帧之间发生变化的区域,压缩位图,将它们交换到磁盘等。App Store中有一些真正的、可用的应用程序使用这种方法,但这种方法天生就是有限的,你最终会花费大量的精力来优化和管理保存的撤消状态堆栈。

当然,还有其他方法值得考虑。最简单的方法是让您的实际文档模型是一堆小的(即非位图)数据结构,这些结构描述了重新创建上下文状态所需的操作。当用户撤消操作时,您只需删除堆栈顶部的操作,并通过回放剩余的堆栈来重新创建图形上下文。对于“加法”类型的应用程序(想想“笔刷”),这是一种不错的方法,但即使在您描述的在画布上移动形状的简单场景中,也会开始下降。它最终还会遇到一些与位图堆栈方法相同的问题--您拥有的操作越多,重新创建状态所需的时间就越长,因此不可避免地会定期生成位图快照,等等。

对于像您所描述的画布上的对象场景(“移动形状”操作),也有多种方法。一种经典的方法是让您的文档模型成为一组更小、更具体的数据结构,用于描述画布的当前状态。想一想Shape类,等等。在最简单的情况下,当用户向画布添加一个矩形时,一个Shape实例被添加到一个z有序的形状数组中。每当形状列表发生更改时,都会通过按Z顺序绘制每个形状来重新生成上下文。要实现撤消功能,每次更改形状数组时,还要使用NSUndoManager记录调用,当回放该调用时,将导致执行相反的操作。

如果您的形状添加操作如下所示:

代码语言:javascript
复制
[shapeArray insertObject: newShape atIndex: 5];

然后,您可以同时对NSUndoManager执行此操作

代码语言:javascript
复制
[[undoManager prepareInvocationWithTarget: shapeArray] removeObjectAtIndex: 5];

当用户单击undo时,NSUndoManager将回放该调用,并且shapeArray将返回其先前的状态。这是经典的NSUndoManager模式。它工作得很好,但也有一些缺点。例如,在应用程序终止时保留撤销堆栈并不是很简单。由于应用程序终止在iOS上很常见,并且用户通常希望应用程序跨终止无缝恢复状态,因此,根据您的要求,撤消堆栈无法在应用程序终止后继续存在的方法可能是不可行的。还有其他更复杂的方法,但它们大多只是其中一个主题的变体。一个值得一读的经典是来自Gang of Four book, Design PatternsCommand Pattern

无论您选择哪种方法,这类应用程序的开发都将是一个挑战。这种图形撤消功能并不是“免费”内置到UIGraphicsContext中的。你已经在评论中问了好几次例子。我很抱歉地说,这是一个足够复杂的概念,对于某些人来说,在StackOverflow答案的限制下提供一个工作示例是不太可能的。希望这些想法和指针能有所帮助。当然,您也可以从许多开源绘图应用程序中获得灵感(尽管我个人并不知道有任何用于iOS的开源绘图应用程序)。

票数 5
EN

Stack Overflow用户

发布于 2013-02-04 13:24:33

UIGraphicsContext没有自己的撤销堆栈。您需要存储在堆栈上绘制的每个元素,并从堆栈中删除和添加要撤消和重做的项。NSUndoManager类可以帮助您管理撤消和重做操作本身的逻辑,但您的责任是编写代码,将绘图操作保存到堆栈中,然后从堆栈中读取数据以在-drawRect:中重新创建绘图。

票数 2
EN

Stack Overflow用户

发布于 2013-02-05 12:17:15

首先设置undomanager对象并初始化它以便使用。

代码语言:javascript
复制
NSUndoManager *undoObject;
undoObject =[[NSUndoManager alloc] init];

每次上下文改变时,用目标函数注册undo对象以保存uigraphics上下文。

代码语言:javascript
复制
[[undoObject prepareWithInvocationTarget:self] performUndoWithObject:currentContext withLastpoint:lastPoint andFirstPoint:firstPoint];

编写userDefined函数定义

代码语言:javascript
复制
-(void)performUndoWithObject:(CGContextRef )context withLastpoint:(CGPoint)previousLastPoint andFirstPoint:(CGPoint)previousFirstPoint
{
    // necessary steps for undoing operation
}

指定单击撤消按钮时的操作。

代码语言:javascript
复制
-(void)undoButtonClicked
{
    if([undoObject canUndo])
    {
        [undoObject undo];
    }   
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14644740

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档