【问题标题】:How to name Undo menu entries for Core Data add/remove items via bindings and NSArrayController?如何通过绑定和 NSArrayController 为 Core Data 添加/删除项目命名撤消菜单条目?
【发布时间】:2014-05-15 20:06:32
【问题描述】:

我有一个由 Core Data 实体填充的 NSTableView 和 Add Item / Remove Item 按钮,所有这些按钮都与一个 NSArrayController 和 Interface Builder 中的绑定连接。

撤消/重做菜单项可以撤消或重做添加/删除项操作。

但菜单条目仅称为“撤消”。 “重做”。
我如何将它们命名为“撤消添加项目”、“撤消删除项目”等。

(我知道,之前有人问过类似的问题,但接受的答案是a single, now rotten linkthe advice to subclass NSManagedObject and override a method that Apples documentation says about: "Important: You must not override this method.“

【问题讨论】:

  • 在 NSArrayController 中似乎有一些解决方案在 NSArrayController insertObject: atArrangedObjectIndex: 和 removeObjectAtArrangedObjectIndex 中实现 KVC 方法。

标签: objective-c cocoa core-data nsarraycontroller nsundomanager


【解决方案1】:

将 NSArrayController 的子类添加为项目中的文件。在 xib 中,在数组控制器的 Identity Inspector 中,将 Class 从 NSArrayController 更改为您的新子类。

覆盖- newObject 方法。

- (id)newObject
{
    id newObj = [super newObject];

    NSUndoManager *undoManager = [[[NSApp delegate] window] undoManager];
    [undoManager setActionName:@"Add Item"];

    return newObj;
}

还有- remove:sender 方法。

- (void)remove:(id)sender
{
    [super remove:sender];

    NSUndoManager *undoManager = [[[NSApp delegate] window] undoManager];
    [undoManager setActionName:@"Remove Item"];
}

【讨论】:

  • 我认为这是更简单,更易于维护的答案。将逻辑放在它所属的地方。除非您开始在模型中维护对撤消/重做名称的支持(所有实体都有某种不错的撤消菜单名称),否则您要么按照您的建议覆盖用户调用的控制器/视图方法,要么监视变化的上下文,并有一个巨大的逻辑球试图描述每一个可能的变化。不过,观察上下文的变化是一个巧妙的技巧,并且可以在模型中得到足够的支持。
【解决方案2】:

注册 NSManagedObjectContextObjectsDidChangeNotification:

[[NSNotificationCenter defaultCenter] addObserver: self
                                         selector: @selector(mocDidChangeNotification:)
                              name:NSManagedObjectContextObjectsDidChangeNotification
                                           object: nil];

并在相应的方法中解析userInfo字典:

- (void)mocDidChangeNotification:(NSNotification *)notification
{
    NSManagedObjectContext* savedContext = [notification object];

    // Ignore change notifications for anything but the mainQueue MOC
    if (savedContext != self.managedObjectContext) {
        return;
    }

    // Ignore updates -- lots of noise from maintaining user-irrelevant data

    // Set actionName for insertion
    for (NSManagedObject* insertedObject in 
           [notification.userInfo valueForKeyPath:NSInsertedObjectsKey])
    {
        NSString* objectClass = NSStringFromClass([insertedObject class]);
        savedContext.undoManager.actionName = savedContext.undoManager.isUndoing ? 
            [NSString stringWithFormat:@"Delete %@", objectClass] : 
            [NSString stringWithFormat:@"Insert %@", objectClass];
    }   

    // Set actionName for deletion
    for (NSManagedObject* deletedObject in 
           [notification.userInfo valueForKeyPath:NSDeletedObjectsKey])
    {
        NSString* objectClass = NSStringFromClass([deletedObject class]);
        savedContext.undoManager.actionName = savedContext.undoManager.isUndoing ? 
            [NSString stringWithFormat:@"Insert %@", objectClass] : 
            [NSString stringWithFormat:@"Delete %@", objectClass];
    }

}

我已经在自己的代码中对此进行了测试——它很粗糙。可以花更多时间使 actionName 更好。我删除了对更新的解析,因为:1)在一对多关系中插入和删除对象会生成其他对象的更新 2)我不关心如何发现此时哪些属性发生了变化

我还有一些对用户不友好的类名,所以现在是为所有实体实现描述函数并使用它而不是类名的好时机。

但这至少适用于项目中的所有对象控制器,并且很容易插入和删除。

[edit] 更新了 mikeD 的建议,以涵盖具有反向名称的重做。谢谢!

【讨论】:

  • 这是个好主意。我使用 [[inserted/deletedObject entity] name] 作为要添加的字符串。
  • 重做出现问题:如果我添加了一个元素——撤消菜单项被正确调用为“撤消插入实体”——然后撤消它,重做菜单项未命名为“重做插入实体”但“重做删除实体”。你有任何提示如何解决这个问题吗?
  • 使用 NSUndoManager 中的- isUndoing 方法来确定使用哪个 ActionName。
  • 用 isUndoing 更新了上面的代码示例。我要感谢 mikeD!
  • 我开始怀疑,尝试在我的实体上维护一个 actionName 方法是否是一个巧妙的技巧,可以使用 KVO 监控属性更改并让实体类描述更改。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
相关资源
最近更新 更多