如何使NSUndoManager的撤销/重做操作名称正常工作?

7

我正在学习Cocoa,在没有太多麻烦的情况下使撤销功能工作起来了。但是setActionName:方法让我感到困惑。这里有一个简单的例子:一个玩具应用程序,其窗口包含一个单独的文本标签和两个按钮。按下On按钮,标签显示“On”。按下Off按钮,标签更改为“Off”。以下是两个相关的方法(我为应用程序编写的唯一代码):

-(IBAction) turnOnLabel:(id)sender
{
    [[self undoManager] registerUndoWithTarget:self selector:@selector(turnOffLabel:) object:self];
    [[self undoManager] setActionName:@"Turn On Label"];
    [theLabel setStringValue:@"On"];
}

-(IBAction) turnOffLabel:(id)sender
{
    [[self undoManager] registerUndoWithTarget:self selector:@selector(turnOnLabel:) object:self];
    [[self undoManager] setActionName:@"Turn Off Label"];
    [theLabel setStringValue:@"Off"];
}

这是我期望的操作:

  • 我点击开启按钮
  • 标签变为“开启”
  • 在编辑菜单中有“撤销开启标签”的选项
  • 我点击该菜单项
  • 标签变为“关闭”
  • 在编辑菜单中有“重做开启标签”的选项

实际上,除了最后一项,所有这些操作都按照我的期望工作。编辑菜单中的项目应该是“重做开启标签”,而不是“重做关闭标签”。(当我点击该菜单项时,标签确实会变为“开启”,但这使得菜单项的名称更加令人费解。)

我是否有什么误解,如何才能让这些菜单项显示成我想要的样子?

3个回答

3

注意:当您进行重做操作时,您的代码必须为撤销菜单项设置一个 actionName。

当您进行撤销或重做操作时,Redo 菜单项中的 actionName 会自动设置。

setActionName: 只更改了 Undo 菜单项中的 actionName。Redo 菜单项的 actionName 是自动化的。

当您在 ![[self undoManager] isUndoing] 时最初设置 setActionName:,此 actionName 将进入 Undo 菜单项。然后当您选择撤销 ([[self undoManager] isUndoing] == YES 时,您不需要设置任何 actionNames),undoManager 会自动将此 actionName 设置为 Redo 菜单项,并将上一个撤消操作的 actionName 设置为 Undo 菜单项。然后当您选择重做时,仍然需要传递一个 actionName 才能转到 Undo 菜单项。

换句话说:只有在您的代码没有进行撤销操作时才需要设置 actionNames(但当最初调用或正在重做时必须设置)。


我在这里只提到了 setActionName: 的功能,而没有提到 redoActionName:undoActionName: 的功能。 - Vassilis

2

在查看了一些示例代码后,我成功解决了这个问题,方法是在每个方法的setActionName:调用中添加条件,就像这样:

if (![[self undoManager] isUndoing])
    [[self undoManager] setActionName:@"Turn On Label"];

我会给出正确答案,解释为什么NSUndoManager需要我这样做。

2

因为你的turnOnLabel:方法可以通过三种可能的方式调用:

1)当与所选控件相关联的控件执行其目标/操作序列时(即,NSUndoManager的isUndoing和isRedoing方法都将返回NO)

2)当您执行撤消操作时,turnOnLabel:方法实际上是由NSUndoManager调用的(即,isUndoing = YES且isRedoing = NO)

3)当您执行重做操作时,turnOnLabel:方法实际上是由NSUndoManager调用的(即,isUndoing = NO且isRedoing = YES)

isUndoing  isRedoing    Action
-------------------------------------------------
   0           0        Turn On Label
   0           1        Turn On Label
   1           0        Turn Off Label
   1           1        <impossible state>

Clarence,感谢你的回答。我理解你的意思,但我不明白为什么在撤销、重做或第一次执行时调用setActionName:会有所不同。无论哪种情况,据我所见,都会向撤销/重做堆栈中添加或删除一个操作,并且该操作的名称应该相同。 - Gabriel Roth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接