Objective c NSUndoManager正在使用单次撤消撤消鼠标拖动期间所做的所有更改

Objective c NSUndoManager正在使用单次撤消撤消鼠标拖动期间所做的所有更改,objective-c,cocoa,nsundomanager,Objective C,Cocoa,Nsundomanager,我有一个NSView对象,它接受鼠标标记的事件,这些事件会更改在其各自视图中绘制的对象的位置。在我的NSViewController类中,它然后检索该新位置并将该新值分配给我的模型数据对象。换句话说,每次调用mouseDragged事件时,ViewController都会更新新位置。因此,在同一范围内,ViewController通过NSUndoManager注册撤消事件,以允许用户执行撤消操作 在撤消管理器中注册多个位置更新操作时,会出现此问题。当用户执行撤消操作时,除了从撤消堆栈中弹出最近的

我有一个NSView对象,它接受鼠标标记的事件,这些事件会更改在其各自视图中绘制的对象的位置。在我的NSViewController类中,它然后检索该新位置并将该新值分配给我的模型数据对象。换句话说,每次调用mouseDragged事件时,ViewController都会更新新位置。因此,在同一范围内,ViewController通过NSUndoManager注册撤消事件,以允许用户执行撤消操作

在撤消管理器中注册多个位置更新操作时,会出现此问题。当用户执行撤消操作时,除了从撤消堆栈中弹出最近的操作外,其余操作也会撤消

以下是我的代码供参考:

// MyViewController.m
- (void)translateObject:(double)dx
          withYPosition:(double)dy
{
    NSMutableDictionary* transformData = [self.transformData transform];

    double t_x = [[transformData objectForKey:@"translate_x"] doubleValue];
    double t_y = [[transformData objectForKey:@"translate_y"] doubleValue];

    t_x -= x;
    t_y -= y;

    [self translateShape:@[[NSNumber numberWithDouble:t_x],[NSNumberNumberWithDouble:t_y]]];
}

- (void)translateShape:(NSArray*)val
{
    NSMutableDictionary* transformData = [self.transformData transform];

    double old_t_x = [[transformData objectForKey:@"translate_x"] doubleValue];
    double old_t_y = [[transformData objectForKey:@"translate_y"] doubleValue];
    double t_x = [[val objectAtIndex:0] doubleValue];
    double t_y = [[val objectAtIndex:1] doubleValue];

    [transformData setObject:[NSNumber numberWithDouble:t_x] forKey:@"translate_x"];
    [transformData setObject:[NSNumber numberWithDouble:t_y] forKey:@"translate_y"];
    [self.transformData setTransform:transformData];
    [self.glView setNeedsDisplay:YES];

    [[self undoManager] registerUndoWithTarget:self
                                  selector:@selector(translateShape:)
                                    object:@[[NSNumber numberWithDouble:old_t_x],
                                             [NSNumber numberWithDouble:old_t_y]]];

    [[self undoManager] setActionName:@"Shape Drag Move"];  
}

/* * * I have also done this:

- (void)translateShape:(NSArray*)val
{
    NSMutableDictionary* transformData = [self.transformData transform];

    double old_t_x = [[transformData objectForKey:@"translate_x"] doubleValue];
    double old_t_y = [[transformData objectForKey:@"translate_y"] doubleValue];
    double t_x = [[val objectAtIndex:0] doubleValue];
    double t_y = [[val objectAtIndex:1] doubleValue];

    [transformData setObject:[NSNumber numberWithDouble:t_x] forKey:@"translate_x"];
    [transformData setObject:[NSNumber numberWithDouble:t_y] forKey:@"translate_y"];
    [self.transformData setTransform:transformData];
    [self.glView setNeedsDisplay:YES];

    [[self undoManager] registerUndoWithTarget:self
                                  selector:@selector(unTranslateShape:)
                                    object:@[[NSNumber numberWithDouble:old_t_x],
                                             [NSNumber numberWithDouble:old_t_y]]];

    [[self undoManager] setActionName:@"Shape Drag Move"];  
}

- (void)unTranslateShape:(NSArray*)val
{
    NSMutableDictionary* transformData = [self.transformData transform];

    double old_t_x = [[transformData objectForKey:@"translate_x"] doubleValue];
    double old_t_y = [[transformData objectForKey:@"translate_y"] doubleValue];
    double t_x = [[val objectAtIndex:0] doubleValue];
    double t_y = [[val objectAtIndex:1] doubleValue];

    [transformData setObject:[NSNumber numberWithDouble:t_x] forKey:@"translate_x"];
    [transformData setObject:[NSNumber numberWithDouble:t_y] forKey:@"translate_y"];
    [self.transformData setTransform:transformData];
    [self.glView setNeedsDisplay:YES];

    [[self undoManager] registerUndoWithTarget:self
                                  selector:@selector(translateShape:)
                                    object:@[[NSNumber numberWithDouble:old_t_x],
                                             [NSNumber numberWithDouble:old_t_y]]];

    [[self undoManager] setActionName:@"Shape Redo Drag Move"]; 
}

* * */
下面是一张拙劣的图表,与我实际观察到的情况相比,我预期撤销会发生什么:


有人能解释一下吗?谢谢。

感谢James Bucanek关于设置NSUndoManager的runLoopModes的提示,我能够理解为什么NSUndoManager在mouseDown、mouseDragged和mouseUp事件中撤消了我所有注册的撤消。在我的例子中,NSUndoManager将按事件注册的所有撤消分组(我想这也意味着在mouseDragged事件中注册的任何撤消操作)。为了简单地在NSView子类中注册撤消,以下操作应该有效:

/** Simple case: Everything handled by NSView subclass **/

- (void)mouseDown:(NSEvent *)event
{
    // Set your undo manager's groupsByEvent property to NO here
}

- (void)mouseDragged:(NSEvent *)event
{
    // Tell your undo manager to begin grouping
    // Register undo and redo actions here
    // Tell your undo manager to end grouping
}

- (void)mouseUp:(NSEvent *)event
{
    // Set your undo manager's groupsByEvent property to YES here
}

由于我使用协议在视图控制器中设置模型的属性,因此我只是在该协议中实现了另一种方法,该方法在NSView的mouseDown/mouseUp事件期间调用,随后将GroupsByEvent设置为NO和YES。如果有人知道这个问题的更好的实现/解决方案,请随时纠正我。希望这有帮助

多亏了James Bucanek关于设置NSUndoManager的runLoopModes的提示,我才明白为什么NSUndoManager在mouseDown、mouseDragged和mouseUp事件中撤消了我所有注册的撤消。在我的例子中,NSUndoManager将按事件注册的所有撤消分组(我想这也意味着在mouseDragged事件中注册的任何撤消操作)。为了简单地在NSView子类中注册撤消,以下操作应该有效:

/** Simple case: Everything handled by NSView subclass **/

- (void)mouseDown:(NSEvent *)event
{
    // Set your undo manager's groupsByEvent property to NO here
}

- (void)mouseDragged:(NSEvent *)event
{
    // Tell your undo manager to begin grouping
    // Register undo and redo actions here
    // Tell your undo manager to end grouping
}

- (void)mouseUp:(NSEvent *)event
{
    // Set your undo manager's groupsByEvent property to YES here
}

由于我使用协议在视图控制器中设置模型的属性,因此我只是在该协议中实现了另一种方法,该方法在NSView的mouseDown/mouseUp事件期间调用,随后将GroupsByEvent设置为NO和YES。如果有人知道这个问题的更好的实现/解决方案,请随时纠正我。希望这有帮助

鼠标标记的事件只是事件,还是包含拖动会话?“多位置更新操作”是一次拖动还是多次拖动?只是猜测一下:尝试将
NSEventTrackingRunLoopMode
添加到撤消管理器的
runLoopModes
属性中。@Willeke:位置/平移值在一次拖动后更新,我想这取决于用户拖动鼠标的时间长短。@JamesBucanek:它不起作用。但也许我应该以此作为一个提示,深入了解Cocoa的NSUndoManager组如何在运行循环过程中撤销操作。谢谢。我再也找不到这些文档了(我讨厌Apple API文档不再链接到相关的开发人员指南),但如果内存足够,就会创建一个撤销组,并在每次迭代时通过运行循环关闭。如果您以某种方式绕过运行循环,您可能需要自己关闭每个组。鼠标标记的事件只是事件,还是涉及到拖动会话?“多位置更新操作”是一次拖动还是多次拖动?只是猜测一下:尝试将
NSEventTrackingRunLoopMode
添加到撤消管理器的
runLoopModes
属性中。@Willeke:位置/平移值在一次拖动后更新,我想这取决于用户拖动鼠标的时间长短。@JamesBucanek:它不起作用。但也许我应该以此作为一个提示,深入了解Cocoa的NSUndoManager组如何在运行循环过程中撤销操作。谢谢。我再也找不到这些文档了(我讨厌Apple API文档不再链接到相关的开发人员指南),但如果内存足够,就会创建一个撤销组,并在每次迭代时通过运行循环关闭。如果你以某种方式绕过了跑步循环,你可能需要自己关闭每个小组。