アプリが特定の状態にあるときに、特定の元に戻す/やり直し操作を条件付きで無効にしたいという同様の状況がありました(他の操作の元に戻す/やり直しは引き続き許可します)。
ビューに実装- (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クラスに変更しましたが、対処する警告はありませんでした。