iOS 26 的 popover 系统颜色异常且不随深浅色模式更新,如何处理?
可以同时尝试两项处理:让 presenting controller 将外观变化同步给 popover,并用 dynamic provider 包装 .systemBackground,作为 popover 根视图的背景。前者针对外观不更新,后者针对初始背景颜色异常。
同一复现问题的处理记录报告了这组临时方案的效果,但没有给出确定的底层原因。可以将它作为兼容处理进一步验证,但不能据此认定所有 iOS 26 版本都存在相同问题,或 Apple 已确认该问题。
先区分颜色解析和 trait 更新
.systemBackground 是动态颜色,最终颜色取决于使用它的 trait 环境。因此,即使两个视图使用同一个颜色属性,也需要比较它们实际使用的 traits,才能判断最终显示的颜色。动态颜色可以通过指定的 UITraitCollection 解析为具体颜色,参见 Apple 的 resolvedColor(with:) 文档。
排查时,分别记录 presenting controller、popover controller、popover 根视图和 UILabel 的 traitCollection.userInterfaceStyle,然后在 popover 保持显示时切换外观:
- 如果 presenting controller 的值变化,而 popover 内仍保留旧值,优先处理外观同步。
- 如果外观值已经一致,标签背景仍异常,再单独验证背景颜色包装是否有效。
- 同时检查窗口、控制器和视图上是否设置了
overrideUserInterfaceStyle或traitOverrides,避免已有覆盖影响判断。
registerForTraitChanges 监听的是注册对象自身的 trait 变化。如果回调未触发,说明该对象没有观察到所监听 trait 的值变化;仅凭这一点,不能确定 UIKit 内部在哪一层出了问题。参见 UITraitChangeObservable 文档。
从 presenting controller 同步外观
如果 presenting controller 能正常收到深浅色变化,可以在那里监听,再设置 popover 的 traitOverrides.userInterfaceStyle。
将原来的 ViewController 替换为下面的实现,保留原有的 UIPopoverPresentationControllerDelegate 扩展:
class ViewController: UIViewController {
private weak var activePopover: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
registerForTraitChanges([UITraitUserInterfaceStyle.self]) {
(controller: ViewController, _: UITraitCollection) in
controller.synchronizePopoverStyle()
}
}
private func synchronizePopoverStyle() {
activePopover?.traitOverrides.userInterfaceStyle =
traitCollection.userInterfaceStyle
}
@IBAction func doButton(_ sender: UIButton) {
let contentController = ViewController2()
contentController.modalPresentationStyle = .popover
if let presentation = contentController.popoverPresentationController {
presentation.sourceItem = sender
presentation.delegate = self
}
activePopover = contentController
synchronizePopoverStyle()
present(contentController, animated: true)
}
}
代码在弹出前同步一次外观,之后通过监听更新。weak 引用用于访问当前 popover,控制器的展示生命周期仍由 UIKit 管理。
Apple 将 traitOverrides 定义为设置控制器及其视图 trait 覆盖的容器。上述方案主动指定 popover 应采用的外观,适用于它需要跟随 presenting controller 的场景。如果应用允许弹出内容使用独立主题,应改为读取应用自己的主题设置。
包装 popover 根视图的背景颜色
对于标签背景异常,可以再尝试创建一个动态颜色,在 provider 内使用传入的 traits 解析 .systemBackground:
extension UIColor {
static var popoverContentBackground: UIColor {
UIColor(dynamicProvider: { environment in
UIColor.systemBackground.resolvedColor(with: environment)
})
}
}
然后只替换 ViewController2.viewDidLoad() 中根视图的背景设置:
view.backgroundColor = .popoverContentBackground
其余布局代码保持原样,标签背景仍可保留:
label.backgroundColor = .systemBackground
这项处理改变了根视图背景颜色的提供方式。同一复现问题的处理记录报告,这样也改善了标签的背景显示。具体原因仍不明确,不能将其视为所有系统颜色异常的通用修复。
按照 Apple 的 init(dynamicProvider:) 文档,provider 应使用参数提供的 traits 决定颜色。将 resolvedColor(with:) 放在 provider 内,可以在每次解析时使用对应环境;如果只在初始化时解析一次并保存结果,保存的就是那次解析得到的具体颜色。
验证范围和注意事项
在实际支持的系统版本和设备上,至少验证浅色启动、深色启动、popover 显示期间双向切换,以及关闭后重新打开。分别启用外观同步和背景包装,确认每项改动解决了哪个现象。
这组代码面向题设的 iOS 26 场景,没有在这里运行验证。如果 presenting controller 本身也收不到外观变化,应先检查它的 trait 环境和外观覆盖设置,不能继续依赖它转发。
如果标签只需要显示文字,可以另行评估使用 .clear 背景,但这只会去掉标签自身的背景色块,不能解决 popover 的外观更新问题。也不要把背景固定为 .white 或 .black 来替代动态适配。
如果问题仍能在最小示例中稳定复现,提交反馈时附上系统完整版本、构建所用 SDK、设备信息,以及切换前后的 trait 记录。这些信息有助于判断异常发生在外观传播还是颜色呈现阶段。
备注:内容仅供参考。
