引言
大家好, 我是一牛,很高兴又和大家见面了。在开发Mac应用时,菜单作为用户与应用交互的入口,发挥着不可或缺的作用。今天让我们来学习菜单的基础知识 - 菜单是如何工作的。
菜单分类
在AppKit , NSMenu
和 NSMenu 是构建各类菜单的基础组件。NSMenu
的实例管理它包含的菜单项和如何绘制这些菜单项。而NSMenuItem
的实例代表单个菜单项,包含绘制信息和状态,但不直接处理绘制和事件。
在Mac应用中,有以下几类。
- 应用菜单 - 在屏幕的最上方
- 弹出式菜单 - 可出现在窗口的任何位置
- 状态栏菜单 - 适用于没有主窗口的应用
- 上下文菜单 - 用户右键点击视图时出现
- 程序坞菜单 - 右键点击程序坞应用图标时出现
在常规Mac应用开发中,最常用的是应用菜单栏、上下文菜单和弹出式菜单。
创建上下文菜单示例
objective-c
// 创建上下文菜单
-(NSMenu *)contextMenu {
NSMenu *contextMenu = [[NSMenu alloc] initWithTitle:@"上下文菜单"];
[contextMenu addItemWithTitle:@"复制" action:@selector(copyAction:) keyEquivalent:@""];
[contextMenu addItemWithTitle:@"粘贴" action:@selector(pasteAction:) keyEquivalent:@""];
[contextMenu addItem:[NSMenuItem separatorItem]];
[contextMenu addItemWithTitle:@"删除" action:@selector(deleteAction:) keyEquivalent:@""];
return contextMenu;
}
// 弹出上下文菜单
-(void)rightMouseDown:(NSEvent *)event {
NSLog(@"%@",self.view.window.firstResponder);
[NSMenu popUpContextMenu:[self contextMenu] withEvent:event forView:self.view];
}
与NSButton
类似,这里没有设置动作消息的目标,因此当右键点击视图弹出菜单时,第一个处理消息的响应者就是该视图。如果视图不处理该消息,消息会沿着响应者链传递,直到有响应者处理或最终被丢弃。
菜单项状态
菜单项有三种状态,NSOnState
、NSOffState
和 NSMixedState
。如果菜单项的状态是NSOnState
,那么该菜单项旁边会出现勾选标志;如果菜单项的状态是NSOffState
,该菜单项不会显示任何图标;如果该菜单项是和NSMixedState
, 那么该菜单项旁边会出现一个短横线。对于一个整体中只有部分是一种状态时,NSMixedState
非常有用。我们可以使用代码设置菜单项的状态。
objective-c
[menuItem setState:NSMixedState];
菜单项启用机制
-
自动启用
当菜单能找到响应菜单项动作消息的对象时,菜单项会自动启用。如需更精细的控制,可以实现
validateUserInterfaceItem
方法。 -
手动启动
使用
setEnabled:
方法可单独启用或禁用菜单项:
默认情况下,菜单会自动启用或禁用其包含的菜单项。可通过setAutoenablesItems:
切换为手动模式。
自动启用菜单的工作机制
当自动启用生效时,用户事件(如点击菜单)会触发菜单项的自动更新。系统会检查:
- 菜单项的目标是否实现了对应动作方法
- 目标是否实现了
validateUserInterfaceItem:
协议方法
具体规则:
- 当设置了菜单项的目标后,菜单首先会检查该目标有没有实现菜单项的动作消息。如果没有实现,那么该菜单项会被禁用。如果已经实现了,菜单会检查该目标是否实现了
validateUserInterfaceItem:
。如果没有实现这个方法,菜单项被启用。如果实现了这个方法,会根据该方法返回的布尔值决定菜单项的启用或者禁用 - 如果菜单项的目标没有设置,并且菜单不是上下文菜单时,这个时候响应者链会启用。菜单会使用响应者链找到响应动作消息的目标。如果在响应者链没有找到目标,该菜单项会被禁用。如果找到了目标,和前面一条规则一样,会检查该目标是否实现了
validateUserInterfaceItem:
。 - 当菜单项没有设置目标且菜单是上下文菜单,响应者链会从菜单关联的视图开始。后续处理流程和规则2一样。
我们创建一个包含Storyboard
新工程,给Help
菜单增加一个菜单项-打开帮助网站,并且将它的动作消息连接到第一响应者的openHelpLink
(点击第一响应者,增加一个动作方法)。我们可以AppDelegate中实现
objective-c
-(void)openHelpLink:(id)sender {
NSLog(@"打开帮助文档");
}
-(BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
// 根据需要启用还是禁用菜单项
return true;
}
由于该菜单是应用菜单,且它的目标没有设置,它会从第一响应者开始,使用响应者链来寻找实现动作消息的目标。此时由于我们没有设置第一响应者,第一响应者默认是窗口。最终消息会沿着响应者链传递到App Delegate
,菜单项自动启用。
结语
通过本文,我们深入了解了Mac应用开发中菜单系统的工作原理和实现方式。掌握这些知识将帮助您创建更专业、更符合用户期待的Mac应用程序。同时我们也要掌握好响应者链的知识,进而掌握自动启用菜单项。希望这些内容对您的开发工作有所帮助!