
写了一个桌面应用发现托盘图标颜色跟系统主题不一致。很多人第一反应是手动检测明暗模式然后换图标但其实 macOS 早就给了解决方案 —— 只是你可能不知道而已。看下去让你少走弯路。起因托盘图标颜色不对劲事情是这样的。我最近在做 PinWall —— 一个 macOS 桌面透明便签应用基于 Tauri v2 构建。有一次我打开应用发现菜单栏上的托盘图标颜色不对。别的 app 的图标是白色的我的 app 还是黑色的一眼就能看到差异强迫症直接犯病。对就是那种明明别人都对就我不对的感觉。很多人的第一反应手动检测明暗模式既然系统有明暗两种模式那我检测到当前是哪种模式然后换不同的图标不就行了我之前就是这么做的。思路很朴素代码也很直观#[cfg(target_os macos)]fnis_dark_mode()-bool{matchstd::process::Command::new(defaults).args([read,-g,AppleAquaColorVariant]).output(){Ok(out)ifout.status.success(){// AppleAquaColorVariant 6 表示深色模式String::from_utf8_lossy(out.stdout).trim()6}_false,}}fntray_icon_bytes()-static[u8]{ifcfg!(target_osmacos)is_dark_mode(){include_bytes!(../icons/tray/icon_white_32.png)}else{include_bytes!(../icons/tray/icon_32.png)}}启动时读一下AppleAquaColorVariant如果是6就用白色图标否则用黑色图标。看起来没什么问题对吧然后问题就来了。AppleAquaColorVariant读的是全局偏好设置也就是系统设置里那个深色/浅色开关的值。它不管你有没有外接显示器也不管每个显示器各自的模式是什么。它只会告诉你“用户在全局设置里选了深色模式”。所以当我本机是深色、外接是浅色的时候is_dark_mode()永远返回true托盘图标永远用白色——浅色显示器上看起来就不对了。第二次尝试监听系统外观变化那我在程序里监听外观变化行不行比如用户拔了外接显示器或者切换了某个显示器的模式我能收到通知然后换图标查了一圈文档macOS 确实有NSWorkspace.didChangeScreenParametersNotification之类的通知但问题是这些通知是 Cocoa / AppKit 层的 API我用的是 TauriRust 后端要通过 objc 桥接才能调用最关键的是——即使能监听到我也没法知道每个显示器各自是深色还是浅色。macOS 没有暴露当前哪个显示器是什么模式这样的 API折腾了半天发现手动适配这条路走不通。手动检测永远会有边缘情况覆盖不到。正确的解法让 macOS 自己来处理手动适配不行那就换个思路——不手动适配交给系统。macOS 原生提供了一种图标处理方式叫Template Image。当你把一个图标标记为 template 时macOS 就不会把它当成一张普通的彩色图片来渲染而是当成一个模板或者说遮罩。它会提取图片中的透明像素轮廓然后根据当前环境自动着色在深色背景下 → 渲染成白色在浅色背景下 → 渲染成黑色而且最重要的是这个着色是系统自动完成的每个显示器各自算各自的。你不需要手动检测、不需要监听变化、不需要关心用户有几个显示器、每个显示器是什么模式。macOS 自己就搞定了。具体怎么做呢Tauri 提供了一个 API/// 加载托盘图标 —— 白色模板图fntray_icon()-Imagestatic{Image::from_bytes(include_bytes!(../icons/tray/icon_template_32.png)).unwrap()}pubfnsetup_tray(app:tauri::App)-tauri::Result(){letmenubuild_tray_menu(app.handle(),lang)?;leticontray_icon();let_trayTrayIconBuilder::with_id(TRAY_ID).menu(menu).icon(icon).icon_as_template(true)// ← 关键标记为 template.on_menu_event(|app,event|{/* ... */}).build(app)?;Ok(())}就这么一行.icon_as_template(true)解决了所有手动适配的麻烦。图标长什么样图标本身也很简单——就是一个白色的 PNG跟原来黑色图标的轮廓一模一样。因为 template 模式下macOS 只看透明像素的轮廓不在乎你是黑是白。白色图标在深色环境下会被渲染成黑色在浅色环境下会被渲染成白色完美适配。一些感悟这件事让我意识到两个问题1. 很多解决方案其实已经是平台的一部分了macOS 从很早以前就有 Template Image 这个概念了iOS 的UIImageTemplate也是同理。只是我以前一直做 Web 前端开发对系统原生的这些特性了解不多。这次用 Tauri 开发桌面端翻阅文档才发现原来 Tauri 已经为我们封装好了这个底层 API。很多时候我们遇到的问题平台早就给了解决方案只是我们不知道而已。2. 手动适配永远不如系统原生方案可靠我之前的方案之所以失败根本原因是手动检测这个思路本身就是错的。系统的明暗模式不是一个全局布尔值而是每个显示器独立的状态。除非你用系统提供的方式去处理否则永远会有边缘情况覆盖不到。总结如果你也在用 Tauri 写 macOS 应用托盘图标记得加上.icon_as_template(true)。一行代码省掉一堆麻烦。项目地址PinWall如果这篇文章帮到了你点个赞就是对我最大的鼓励