문제

I've implemented undo/redo the standard way (NSUndoManager) but can't figure out how I disable undo/redos when my app is in a specific state.

Users draw things in my app and when what they've drawn is uploading I disable the UI and of course don't want the user to be able to undo/redo.

I use a NSView's Undo Manager so I guess one way could be to just make that view resign first responder. Is there another way?

도움이 되었습니까?

해결책 2

You can finalize undo and redo with

 - (void) removeAllActions;

or remove actions for a specific target with

 - (void) removeAllActionsWithTarget: (id) target;

If you simply want to disable any actions for a time, leaving the undo stack unchanged, simply disable the Undo/Redo menu items using NSMenuValidationProtocol's

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

다른 팁

If the view is the first responder, you can implement the validateMenuItem: protocol to disable or enable the menu items according to your current state.

 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
     SEL action = menuItem.action;

     if (action == @selector(undo:) ||
         action == @selector(redo:)) {
          return !uploadingImage;
     }
     return YES;
 }

The best approach I can think of is making the view's -undoManager method return nil during uploads, which will remove it from the responder chain and cause undo/redo options to be disabled for that view.

(I haven't tested this, but I'm 99% sure that the menus will ask your view for the undo manager whenever it validates the menu options.)

I had a similar situation where I wanted to conditionally disable certain undo/redo operations when the app is in a specific state (while still allowing undo/redo for other operations).

The method of implementing - (BOOL)validateMenuItem:(NSMenuItem *)item on a view doesn't work for me (I have a document-based app on 10.12). Per the docs at https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MenuList/Articles/EnablingMenuItems.html:

If there is an object in the responder chain that implements the item’s action, NSMenu then checks to see if that object implements the validateMenuItem: or validateUserInterfaceItem: method. If it does not, then the menu item is enabled. If it does, then the enabled status of the menu item is determined by the return value of the method.

The view would have to add an undo method the does the right thing as well.

When I probed the responder chain, I found that my NSWindow was the object that responded to undo: (though it's not part of the documented interface), so my current plan is to use a custom NSWindow subclass with the imeplementation of validateMenuItem, along the lines of:

#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

However there are warnings that the undo: redo: methods aren't implemented. These can be eliminated by creating a category on NSWindow, such as:

@interface NSWindow (SBXUndoable)

- (void)undo:(id)sender;
- (void)redo:(id)sender;

@end

Not sure if there are any issues with doing that (I didn't notice any), but it does eliminate the warnings. I've since changed the class to a Swift class, which didn't have any warnings to deal with.

The documentation is your friend. The disableUndoRegistration method of NSUndoManager has "disable" in its name. It's up to your app's controllers to decide when it's appropriate to disable and re-enable undo registration.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top