← 所有文章

菜单项图像在 macOS 27 与 iPadOS 27 中消失了

如果针对 macOS 27 SDK 链接,一个只有图像、没有标题的菜单项在 macOS 27 上会渲染成一片空白。在更早的 SDK 上,Apple 为这种情况做了兜底:当菜单项的 title 与 attributed title 均为空时,系统会自动显示图像。1 而针对 27 重新构建之后,这层保护便不再生效。

macOS 27 与 iPadOS 27 默认隐藏大部分菜单项图像,而消失的究竟是哪些,取决于您链接的是哪个 SDK。 Apple 把这一结果描述为“与 macOS 26.0 之前的行为类似”——换句话说,macOS 26 大范围地给菜单加上了图像,27 又把其中大部分收了回去。1

受影响的框架共有三套,每套的规避方式各不相同。如果您搜索的是 preferredImageVisibility,而手头写的是 SwiftUI,那么这个属性对您并不存在。

摘要

对于在 macOS 26.0 及更高版本上链接的 App,macOS 27 默认隐藏菜单项的符号图像;在 macOS 27 SDK 上链接的 App,连非符号图像也会一并隐藏。12 纯图标菜单项在 27 之前的 SDK 上受到自动保护,一旦针对 27 重新构建,这份保护即告失效。3 iPadOS 27 默认隐藏设置在菜单元素上的图像。4 AppKit 与 UIKit 提供 preferredImageVisibility,取值为 .automatic.visible.hidden;SwiftUI 则改用 labelStyle(.titleAndIcon)5 “设置”“共享”“打印”在全系统范围内保留各自的图像,因此您会看到部分图标仍在,进而误以为自己的图标出了问题。

按 SDK 划分:究竟改变了什么

AppKit 的这一行为分散在三条独立的发行说明条目中,其中两条以修复的形式记录,这也是各家对此改动的总结彼此矛盾的原因。

链接的 SDK 符号图像 非符号图像 纯图标菜单项
macOS 26 之前的 SDK 不受影响 不受影响 不受影响
macOS 26.0 至 26.x 隐藏 显示 自动显示
macOS 27 SDK 隐藏 隐藏 隐藏

基准条目写明:NSMenu 默认隐藏所有菜单项符号图像,非符号图像依旧可见;该改动适用于在 macOS 26.0 及更高版本上链接的应用程序。1

第二条条目扩大了隐藏范围:“对于在 macOS 27 SDK 上链接的应用程序,符号与非符号菜单项图像现在都会被自动隐藏。对于在更早 SDK 上链接的应用程序,非符号图像仍会自动保持可见,以兼容既有的应用程序行为。”2

第三条才是值得读两遍的:3

“对于在 macOS 27 之前的 SDK 上链接的应用程序,如果菜单项的 title 与 attributed title 均为空,NSMenu 现在会自动显示菜单项图像。当图像是菜单项内容的唯一表现形式时,这样可以保持既有的应用程序行为。而针对 macOS 27 SDK 链接时,这些图像将被自动隐藏;采用此类设计的菜单应当使用 preferredImageVisibility API,以确保菜单项图像保持可见。”

Apple 为纯图标菜单项织了一张安全网,随后又在文档里写明:这张网并不覆盖新 SDK。全部内容就是一张图像的菜单项,重新构建之后便渲染为空。您的源代码一个字都没变,触发条件是编译时所针对的 SDK。

纯图标菜单项并不像听上去那么小众。格式菜单里的色块,色块本身就是选项。以头像或状态图形来标识每一项的设备或账户选择器。由一排纯图像项横向排列而成的表情与 emoji 行。显示文件类型图标、文件名另行渲染的最近文稿列表。在这些场景里,图像都不是挂在标签旁边的装饰——它就是标签本身。

这类菜单会退化成一列仍能响应点击的空白行。菜单的高度、分隔线、点击区域都还在,因此在自动化检查看来,它并不像一次渲染失败。断言“菜单包含六项”的 UI 测试照样通过。截图对比能抓到它,对菜单项数量的断言抓不到。

这种悄无声息,正是两者的家族相似之处。macOS 27 同样会在不弹出询问的情况下拒绝跨团队容器访问:API 交还一个看起来完全正常的 URL,失败要等到真正读取时才浮现。两处改动都用沉默取代了可见的信号,也都以另一副面孔出现在开发者面前。

三套框架,三种修复方式

框架 API 取值
AppKit NSMenuItem.preferredImageVisibility .automatic.visible.hidden
UIKit UIMenuElement.preferredImageVisibility .automatic.visible.hidden
SwiftUI labelStyle(.titleAndIcon) 一种标签(label)样式,而非枚举

AppKit 与 UIKit 的形态一致。两个属性都接受 ImageVisibility 枚举,取值为 .automatic.visible.hidden;两者都基于 NSInteger 实现 RawRepresentable,也都是 Sendable67

// AppKit
menuItem.preferredImageVisibility = .visible

// UIKit — also available on the updated initializers
// for UIMenu, UIAction, UICommand, and UIKeyCommand
let action = UIAction(title: "Export", image: exportImage) { _ in export() }
action.preferredImageVisibility = .visible

SwiftUI 走的是另一条路。其发行说明写道:“使用 labelStyle(_:) 视图修饰符搭配 .titleAndIcon 样式,即可表明菜单项 Label 的图标应当始终显示。”5

Menu("File") {
    Button {
        openDocument()
    } label: {
        Label("Open Document", systemImage: "doc")
    }
    .labelStyle(.titleAndIcon)
}

另外请注意,SwiftUI 的默认行为对应的是 AppKit 的基准状态,而非 27 SDK 下的行为:符号图像隐藏,非符号图像仍然可见。5 上面那张按 SDK 链接情况划分的表格描述的是 AppKit,不要想当然地套用过来。

影响范围比听上去更窄

请仔细读各条目对平台的表述,因为它们并不一致。

UIKit 条目覆盖的是 iPadOS 27.0 与 macOS 27.0 上的“菜单栏和上下文菜单”。4 SwiftUI 条目则更具体:iPadOS 27.0 与 macOS 27.0 上的菜单栏,“以及 macOS 27.0 上的上下文菜单”。5 iPadOS 上的 SwiftUI 上下文菜单并未被点名。

UIMenuElement.preferredImageVisibility 在 iOS、iPadOS、Mac Catalyst、tvOS 与 visionOS 27.0 上均可用。7 但 API 出现在某个平台上,并不代表隐藏行为在那里同样生效。Apple 的发行说明描述的是 iPadOS 与 macOS 上的行为。对 tvOS 和 visionOS,应视为“未作说明”,而不是“已经确认”。

Interface Builder 这一面

有一个细节在代码里找不到对应物。对于由 xib 文件创建的菜单项,NSMenu 会读取菜单项检查器中的“macOS 26.0 only”复选框:不勾选则图像保持可见,勾选则隐藏。1

如今,Interface Builder 里的一个属性就能改变运行时的图像可见性。如果您的菜单来自 xib,那么排查就不是一次代码搜索能完成的——总得有人把检查器一个个打开看。

为什么有些图标还在

对于“设置”“共享”“打印”这类全系统通用的菜单项,AppKit 与 UIKit 仍会提供默认可见的图像。14 SwiftUI 亦然。5

实际效果就是排查时的困惑。系统升级之后打开菜单,“设置”和“共享”旁边有图标,自己的菜单项旁边却没有。合乎情理的推断是:图像加载失败了,或者资源目录(asset catalog)坏了,又或者符号名称写错了。可这些都没发生。系统只是在执行一条策略,而这条策略给少数几个众所周知的菜单项开了例外。

决定保留哪些图标

五条发行说明条目都要求开发者去查阅更新后的 Human Interface Guidelines,据此判断哪些菜单项仍应显示图像。145 这份指南具体怎么说,我无法核实:HIG 的菜单页面在客户端渲染,抓取工具几乎拿不到任何正文;而且 HIG 内容也不像 API 参考文档那样提供 JSON 接口。请自己去看原文,不要轻信任何二手总结——包括本文这一篇。

发行说明本身给出的唯一一条具体经验法则来自 SwiftUI 条目:当菜单项“代表的是一个对象或一个概念,而不是一个操作”时,就应当显示图标。5

这条规则是管用的。列出已打开文稿、可用设备或已保存筛选条件的菜单,点名的是对象,图标在这里承载的是身份标识。而由动词构成的菜单——大多数菜单都是如此——每一项旁边加个图标并不会带来多少收益。看起来 Apple 的结论是:macOS 26 把图像用过头了,27 是一次纠偏。

在用 27 SDK 发布之前该做什么

先找出纯图标菜单项。 它们坏得最彻底,因为图像就是全部内容。任何带图像且标题为空的 NSMenuItem,以及以同样方式构建的 UIMenu 元素,都需要显式设置 .visible

逐项决定,不要一刀切。 到处设 .visible 等于推翻平台一次有意为之的改动,把自己退回 macOS 26 的状态。“对象还是操作”这条规则,比全局覆盖更好用。

xib 要单独排查。 “macOS 26.0 only”复选框没法像属性赋值那样用 grep 找到,但它确实会改变行为。

如果两代 SDK 都要支持,就都测一遍。 同一份源代码,针对 26 构建和针对 27 构建,渲染结果并不相同。这让截图与 UI 测试变得依赖 SDK——此前并非如此。

由 SDK 链接来决定行为,正在成为这一版系统里反复出现的形态。iOS 27 中 canOpenURL 的弃用围绕的是同一根轴,可以吸取的教训也是同一条:决定用户所见的,是您在编译期的选择,而不是源代码。

做好发行说明还会变的准备。 三条 AppKit 条目里有两条被记为修复,说明这一行为在 beta 周期中已经改过一次。2026年8月1日,我对照 Beta 4 的发行说明重新核对了全部五条条目,内容与此处引用一致。在据此动手之前,请对照当前版本的发行说明再确认一遍。

排查既有代码库

这项改动足够机械,可以系统性地排查;而且值得在重新构建之前就做,而不是等测试人员报上来一个空白菜单。

从最严重的情况入手。 带图像且标题为空的 NSMenuItem,是唯一会让菜单明显损坏、而不只是变素净的失败情形。在代码里,寻找有图像赋值却没有对应标题的地方:

# Menu items constructed with an image and no title argument
rg 'NSMenuItem\(' --type swift -A3 | rg -B1 'image ='
rg 'setImage|\.image\s*=' --type swift | rg -i 'menu'

这两个模式都不够周全——标题可能在三行之后才设置,也可能从本地化表里读取。请把结果当作待检查的候选清单,而不是结论。

接着是 xib。 对付“macOS 26.0 only”复选框,grep 帮不上任何忙:它待在菜单项检查器里,而不在构建过程能看到的任何属性中。如果菜单出自 Interface Builder,排查就意味着逐个打开菜单、逐项确认。

然后是 UIKit 的菜单构造。 UIMenuUIActionUICommandUIKeyCommand 都新增了接受 preferredImageVisibility 的初始化方法,因此修复可以直接写在构造处,不必再补一次赋值。4

再然后是菜单内部的 SwiftUI Label 这些最容易被漏掉,因为菜单里的 Label 在源代码中和别处的 Label 长得一模一样。修饰符加在标签上,而且只加在您确实想保留图标的地方。

构建两次,两相对照。 最可靠的检查完全不需要读代码。分别针对 26 SDK 与 27 SDK 构建,打开同样的菜单,各拍一张图。同一份源代码产出两个不同的菜单,正是这项改动的要害;并排一比,就能看到 grep 找不出来的东西。

最后这一步还能保住您的截图资产。带有菜单的营销图、文档配图和 App Store 素材,都是在拍摄当时的 SDK 下捕获的。如果它们展示的图标在您正式发布的构建里已经不再渲染,那它们现在就是错的——而构建流程不会为此发出任何提示。

要点回顾

面向 AppKit 开发者: - 针对 macOS 27 SDK 链接,被隐藏的不只是符号图像,非符号图像同样会被隐藏。只排查 SF Symbols 会漏掉一半。 - 纯图标菜单项在 27 SDK 上失去自动保护,会渲染为空。请为每一项设置 preferredImageVisibility = .visible。 - 在 xib 中定义的菜单带有一个“macOS 26.0 only”复选框,它会在任何代码路径之外改变可见性。

面向 UIKit 开发者: - preferredImageVisibility 位于 UIMenuElement 上,也出现在 UIMenuUIActionUICommandUIKeyCommand 的新版初始化方法中。 - 该属性在 tvOS 与 visionOS 上也存在,但 Apple 记录的行为只涉及 iPadOS 与 macOS。先验证,再下结论。

面向 SwiftUI 开发者: - preferredImageVisibility 不是您要用的 API。请在 Label 上使用 labelStyle(.titleAndIcon)。 - SwiftUI 的默认行为是隐藏符号图像、保留非符号图像,对应 AppKit 的基准状态,而非 27 SDK 下的行为。

常见问题

为什么“设置”和“共享”仍然显示图标?

系统对某些通用菜单项作了豁免。AppKit、UIKit 与 SwiftUI 都仍会为“设置”“共享”“打印”这类菜单项提供默认可见的图像。145 看到这些图标、而自己的图标被隐藏,属于预期行为,不是加载失败。

继续用较旧的 SDK 能避开这件事吗?

只能避开一部分,而这个区别很重要。在 macOS 26.0 及更高版本上链接的 App 已经会隐藏符号图像。1 停留在 macOS 27 SDK 之下,可以保住非符号图像的可见性,也保住纯图标菜单项的自动保护。23 但它并不会让符号图像回来。

只有图像、没有标题的菜单项会怎样?

在 macOS 27 之前的 SDK 上,由于 title 与 attributed title 都为空,NSMenu 会自动显示图像。3 在 macOS 27 SDK 上,这层保护不再适用,图像被隐藏,于是留下一个没有任何可见内容的菜单项。请设置 preferredImageVisibility = .visible

tvOS 与 visionOS 上的情况是否相同?

UIMenuElement.preferredImageVisibility 在两个平台的 27.0 上都可用。7 但发行说明条目描述的隐藏行为只涉及 iPadOS 与 macOS。API 在某个平台上存在,并不等于该行为在那里已经生效。

我应该保留哪些图标?

发行说明指向 Human Interface Guidelines,而我无法直接读到它的内容。Apple 在 SwiftUI 条目中给出的经验法则是:当菜单项“代表的是一个对象或一个概念,而不是一个操作”时,就显示图标。5

参考来源


  1. Apple,“macOS 27 Golden Gate Beta 4 Release Notes,” AppKit。Radar 170477566:NSMenu 默认隐藏所有菜单项符号图像,非符号图像保持可见;适用于在 macOS 26.0 及更高版本上链接的应用程序;xib 中“macOS 26.0 only”复选框的行为;preferredImageVisibility 属性;为“设置”“共享”“打印”提供的默认可见图像。机器可读版本位于 developer.apple.com/tutorials/data/documentation/macos-release-notes/macos-27-release-notes.json。2026年8月1日重新核实。 

  2. Apple,macOS 27 Golden Gate Beta 4 Release Notes,AppKit,已解决问题。Radar 179374305(FB23070183):“对于在 macOS 27 SDK 上链接的应用程序,符号与非符号菜单项图像现在都会被自动隐藏。对于在更早 SDK 上链接的应用程序,非符号图像仍会自动保持可见,以兼容既有的应用程序行为。” 

  3. Apple,macOS 27 Golden Gate Beta 4 Release Notes,AppKit,已解决问题。Radar 179936632:在 27 之前的 SDK 上,为 title 与 attributed title 均为空的菜单项自动显示图像;以及针对 macOS 27 SDK 链接时该行为的移除。 

  4. Apple,“iOS & iPadOS 27 Beta 4 Release Notes,” UIKit。Radar 170479084:iPadOS 27.0 与 macOS 27.0 上的菜单栏和上下文菜单默认不显示设置在菜单元素上的图像;UIMenuElement 上的 preferredImageVisibility,以及 UIMenuUIActionUICommandUIKeyCommand 的新版初始化方法。同一条 Radar 也出现在 macOS 27 的发行说明中。 

  5. Apple,iOS & iPadOS 27 Beta 4 Release Notes,SwiftUI。Radar 170480710:SwiftUI 在大多数场景下默认隐藏所有菜单项符号图像,非符号图像保持可见;labelStyle(_:) 搭配 .titleAndIcon;当菜单项“代表的是一个对象或一个概念,而不是一个操作”时显示图标的指引;为常见系统菜单项提供的默认可见图像。 

  6. Apple,“NSMenuItem.preferredImageVisibility”“NSMenuItem.ImageVisibility.” 枚举成员 .automatic.visible.hidden;基于 NSIntegerRawRepresentable;自 macOS 27.0 起可用。 

  7. Apple,“UIMenuElement.preferredImageVisibility”“UIMenuElement.ImageVisibility.” 枚举成员 .automatic.visible.hidden;可用于 iOS 27.0、iPadOS 27.0、Mac Catalyst 27.0、tvOS 27.0、visionOS 27.0。 

相关文章

macOS 27 拒绝跨团队容器访问,而且不再询问用户

macOS 27 取消了读取其他团队 App Group 容器时的授权提示。API 仍会返回一个看似有效的 URL,失败因此推迟到读取时才暴露。

3 分钟阅读

iOS 27 中 SwiftUI 的新变化

iOS 27 重构了 SwiftUI 的列表、文档、工具栏与错误处理:拖拽重排序、可读写的文档模型、工具栏溢出,以及基于条目的弹窗。

10 分钟阅读

The Mac App Store Won't Let Window Managers Exist. I Shipped One Anyway.

App Sandbox forbids the API every window tiler needs, and Apple said ship outside the store. 941 Tiles ships inside it: …

8 分钟阅读