Appkit: 菜单是如何工作的

引言

大家好, 我是一牛,很高兴又和大家见面了。在开发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类似,这里没有设置动作消息的目标,因此当右键点击视图弹出菜单时,第一个处理消息的响应者就是该视图。如果视图不处理该消息,消息会沿着响应者链传递,直到有响应者处理或最终被丢弃。

菜单项状态

菜单项有三种状态,NSOnStateNSOffStateNSMixedState。如果菜单项的状态是NSOnState,那么该菜单项旁边会出现勾选标志;如果菜单项的状态是NSOffState,该菜单项不会显示任何图标;如果该菜单项是和NSMixedState, 那么该菜单项旁边会出现一个短横线。对于一个整体中只有部分是一种状态时,NSMixedState非常有用。我们可以使用代码设置菜单项的状态。

objective-c 复制代码
[menuItem setState:NSMixedState];

菜单项启用机制

  • 自动启用

    当菜单能找到响应菜单项动作消息的对象时,菜单项会自动启用。如需更精细的控制,可以实现validateUserInterfaceItem方法。

  • 手动启动

    使用setEnabled:方法可单独启用或禁用菜单项:

默认情况下,菜单会自动启用或禁用其包含的菜单项。可通过setAutoenablesItems:切换为手动模式。

自动启用菜单的工作机制

当自动启用生效时,用户事件(如点击菜单)会触发菜单项的自动更新。系统会检查:

  1. 菜单项的目标是否实现了对应动作方法
  2. 目标是否实现了validateUserInterfaceItem:协议方法

具体规则:

  1. 当设置了菜单项的目标后,菜单首先会检查该目标有没有实现菜单项的动作消息。如果没有实现,那么该菜单项会被禁用。如果已经实现了,菜单会检查该目标是否实现了validateUserInterfaceItem:。如果没有实现这个方法,菜单项被启用。如果实现了这个方法,会根据该方法返回的布尔值决定菜单项的启用或者禁用
  2. 如果菜单项的目标没有设置,并且菜单不是上下文菜单时,这个时候响应者链会启用。菜单会使用响应者链找到响应动作消息的目标。如果在响应者链没有找到目标,该菜单项会被禁用。如果找到了目标,和前面一条规则一样,会检查该目标是否实现了validateUserInterfaceItem:
  3. 当菜单项没有设置目标且菜单是上下文菜单,响应者链会从菜单关联的视图开始。后续处理流程和规则2一样。

我们创建一个包含Storyboard新工程,给Help 菜单增加一个菜单项-打开帮助网站,并且将它的动作消息连接到第一响应者的openHelpLink(点击第一响应者,增加一个动作方法)。我们可以AppDelegate中实现

objective-c 复制代码
-(void)openHelpLink:(id)sender {
    NSLog(@"打开帮助文档");
}
-(BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
  // 根据需要启用还是禁用菜单项
    return true;
}

由于该菜单是应用菜单,且它的目标没有设置,它会从第一响应者开始,使用响应者链来寻找实现动作消息的目标。此时由于我们没有设置第一响应者,第一响应者默认是窗口。最终消息会沿着响应者链传递到App Delegate,菜单项自动启用。

结语

通过本文,我们深入了解了Mac应用开发中菜单系统的工作原理和实现方式。掌握这些知识将帮助您创建更专业、更符合用户期待的Mac应用程序。同时我们也要掌握好响应者链的知识,进而掌握自动启用菜单项。希望这些内容对您的开发工作有所帮助!

相关推荐
2501_916007476 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
望获linux7 小时前
【Linux基础知识系列:第一百四十篇】理解SELinux与系统安全
linux·运维·服务器·数据库·chrome·macos
奇舞精选9 小时前
CEF框架实践:构建Mac混合桌面应用
macos·浏览器
2501_9160088911 小时前
HTTPS 双向认证抓包实战,原理、难点、工具与可操作的排查流程
网络协议·http·ios·小程序·https·uni-app·iphone
2501_9151063211 小时前
HTTPS 能抓包吗?实战答案与逐步可行方案(HTTPS 抓包原理、证书Pinning双向认证应对、工具对比)
网络协议·http·ios·小程序·https·uni-app·iphone
游戏开发爱好者811 小时前
App HTTPS 抓包实战,原理、常见问题与可行工具路线(开发 测试 安全 角度)
网络协议·安全·ios·小程序·https·uni-app·iphone
2501_9151063211 小时前
App HTTPS 抓包实战指南,原理、常见阻碍、逐步排查与工具组合
网络协议·http·ios·小程序·https·uni-app·iphone
CocoaKier13 小时前
苹果海外老账号续费,踩了个大坑!
ios·apple
2501_9275393015 小时前
Permute 媒体文件格式转换【音视频图像文件转换】(Mac电脑)
macos·音视频·媒体·格式转换
布拉格沃兹基硕德18 小时前
MacOS报错“zsh: command not found: brew”【已解决】
macos·bug