NSUndoManager使用单个撤消操作

时间:2019-02-02 01:14:38

标签: objective-c cocoa nsundomanager

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

在撤消管理器中注册了多个position-update-actions时,就会出现此问题。当用户执行撤消操作时,其余操作也将撤消,而不仅仅是从撤消堆栈中弹出最近的操作。

作为参考,这是我的代码:

// 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"]; 
}

* * */

下面是一张我做得不好的图,它说明了我预期的撤消操作与我实际观察到的情况:

enter image description here

有人可以阐明这一点吗?谢谢。

1 个答案:

答案 0 :(得分:0)

感谢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。如果有人知道该问题的更好的实现/解决方案,请随时纠正我。希望这会有所帮助!