【问题标题】:Disable undo/redo in Cocoa app在 Cocoa 应用程序中禁用撤消/重做
【发布时间】:2013-02-25 18:37:24
【问题描述】:

我已经以标准方式 (NSUndoManager) 实现了撤消/重做,但无法弄清楚当我的应用处于特定状态时如何禁用撤消/重做。

用户在我的应用中绘制内容,当他们绘制的内容正在上传时,我禁用 UI,当然不希望用户能够撤消/重做。

我使用 NSView 的撤消管理器,所以我想一种方法可能是让该视图退出第一响应者。还有其他方法吗?

【问题讨论】:

    标签: cocoa undo nsundomanager


    【解决方案1】:

    如果视图是第一响应者,你可以实现validateMenuItem:协议根据你当前的状态禁用或启用菜单项。

     - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
         SEL action = menuItem.action;
    
         if (action == @selector(undo:) ||
             action == @selector(redo:)) {
              return !uploadingImage;
         }
         return YES;
     }
    

    【讨论】:

      【解决方案2】:

      您可以使用

      完成撤消和重做
       - (void) removeAllActions;
      

      或删除针对特定目标的操作

       - (void) removeAllActionsWithTarget: (id) target;
      

      如果您只是想暂时禁用任何操作,保持撤消堆栈不变,只需使用 NSMenuValidationProtocol 禁用撤消/重做菜单项

       - (BOOL)validateMenuItem:(NSMenuItem *)menuItem;
      

      【讨论】:

      • 是的。做到了。必须实现 (IBAction)undo:(id)sender 。
      【解决方案3】:

      我能想到的最佳方法是让视图的 -undoManager 方法在上传期间返回 nil,这会将其从响应者链中移除,并导致该视图的撤消/重做选项被禁用。

      (我没有对此进行测试,但我有 99% 的把握,当菜单验证菜单选项时,它会向您的视图询问撤消管理器。)

      【讨论】:

        【解决方案4】:

        我有类似的情况,我想在应用处于特定状态时有条件地禁用某些撤消/重做操作(同时仍允许撤消/重做其他操作)。

        在视图上实现- (BOOL)validateMenuItem:(NSMenuItem *)item 的方法对我不起作用(我在 10.12 上有一个基于文档的应用程序)。根据https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MenuList/Articles/EnablingMenuItems.html 的文档:

        如果响应者链中有一个对象实现了项目的操作,NSMenu 会检查该对象是否实现了 validateMenuItem: 或 validateUserInterfaceItem: 方法。如果没有,则启用菜单项。如果是,则菜单项的启用状态由方法的返回值确定。

        视图必须添加一个撤消方法,它也能做正确的事情。

        当我探测响应者链时,我发现我的 NSWindow 是响应 undo: 的对象(尽管它不是文档化接口的一部分),所以我目前的计划是使用自定义 NSWindow 子类来实现validateMenuItem,大致如下:

        #import "Window.h"
        
        @implementation SBXWindow
        
        - (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag screen:(NSScreen *)screen
        {
            self = [super initWithContentRect:contentRect styleMask:style backing:bufferingType defer:flag screen:screen];
        
            return self;
        }
        
        
        - (BOOL)validateMenuItem:(NSMenuItem *)item
        {
            // Call super imeplementation as it appears to update the menu item title (and potentially other stuff)
            BOOL result = [super validateMenuItem:item];
            if (result == NO) {
                return NO;
            }
        
            if (item.action == @selector(undo:) || item.action == @selector(redo:)) {
                // Add custom logic here
            }
        
            return result;
        }
        
        @end
        

        但是,有警告指出 undo: redo: 方法未实现。这些可以通过在 NSWindow 上创建一个类别来消除,例如:

        @interface NSWindow (SBXUndoable)
        
        - (void)undo:(id)sender;
        - (void)redo:(id)sender;
        
        @end
        

        不确定这样做是否有任何问题(我没有注意到任何问题),但它确实消除了警告。从那以后,我将该类更改为 Swift 类,它没有任何警告要处理。

        【讨论】:

          【解决方案5】:

          文档是您的朋友。 NSUndoManager 的disableUndoRegistration 方法的名称中有“禁用”。由您应用的控制器决定何时适合禁用和重新启用撤消注册。

          【讨论】:

          • 这是关于撤消事件的设置,而不是撤消/重做菜单的禁用。
          猜你喜欢
          • 1970-01-01
          • 2018-09-09
          • 2015-03-30
          • 1970-01-01
          • 2011-04-03
          • 1970-01-01
          • 2013-08-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多