如何修复iOS 15+中UIImagePickerController导航栏透明问题
iOS 15 环境下 UIImagePickerController 导航栏透明问题修复
问题现象
在iOS 15设备上使用UIImagePickerController,以overCurrentContext样式模态弹出相册时,组件导航栏背景变为透明,异常效果如下:
预期效果为导航栏保持白色,与iOS 15以下版本表现一致。直接在初始化picker后设置导航栏appearance的写法无法生效,原有无效代码参考:
let picker = UIImagePickerController() picker.sourceType = .photoLibrary picker.delegate = self picker.modalPresentationStyle = .overCurrentContext if #available(iOS 13.0, *) { let appearance = UINavigationBarAppearance() appearance.configureWithOpaqueBackground() appearance.backgroundColor = .white appearance.shadowColor = .white appearance.shadowImage = UIImage() picker.navigationBar.standardAppearance = appearance picker.navigationBar.scrollEdgeAppearance = appearance } viewController.present(picker, animated: true, completion: nil)
失效原因
代码不生效的核心原因是设置时机错误:UIImagePickerController会在自身viewDidLoad生命周期执行时,重置导航栏的外观配置,初始化后、present前设置的样式会被系统覆盖。同时iOS 15新增了紧凑尺寸下的滚动边缘导航栏样式属性,且overCurrentContext模态模式下,系统对半透明导航栏的渲染逻辑有调整,缺省配置会触发透明效果。
可行修复方案
推荐使用子类化方案,不会影响全局其他导航栏样式,稳定性最高:
- 创建
UIImagePickerController的子类,在viewWillAppear生命周期内强制设置导航栏样式
class WhiteStyleImagePicker: UIImagePickerController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) guard #available(iOS 13.0, *) else { return } let navAppearance = UINavigationBarAppearance() navAppearance.configureWithOpaqueBackground() navAppearance.backgroundColor = .white // 不需要分割线可以保留下面两行,需要的话删除即可 navAppearance.shadowColor = .white navAppearance.shadowImage = UIImage() navigationBar.standardAppearance = navAppearance navigationBar.scrollEdgeAppearance = navAppearance navigationBar.compactAppearance = navAppearance if #available(iOS 15.0, *) { navigationBar.compactScrollEdgeAppearance = navAppearance } // 关键:关闭导航栏半透明,否则iOS15 overCurrentContext模式下仍会透明 navigationBar.isTranslucent = false } }
- 调用时替换原有
UIImagePickerController即可
let picker = WhiteStyleImagePicker() picker.sourceType = .photoLibrary picker.delegate = self picker.modalPresentationStyle = .overCurrentContext viewController.present(picker, animated: true)
如果不想创建子类,也可以把样式设置逻辑放到present的完成回调里执行,但是稳定性略低于子类方案:
let picker = UIImagePickerController() picker.sourceType = .photoLibrary picker.delegate = self picker.modalPresentationStyle = .overCurrentContext viewController.present(picker, animated: true) { guard #available(iOS 13.0, *) else { return } let navAppearance = UINavigationBarAppearance() navAppearance.configureWithOpaqueBackground() navAppearance.backgroundColor = .white navAppearance.shadowColor = .white navAppearance.shadowImage = UIImage() picker.navigationBar.standardAppearance = navAppearance picker.navigationBar.scrollEdgeAppearance = navAppearance picker.navigationBar.compactAppearance = navAppearance if #available(iOS 15.0, *) { picker.navigationBar.compactScrollEdgeAppearance = navAppearance } picker.navigationBar.isTranslucent = false }
注意事项
- 必须将
navigationBar.isTranslucent设置为false,否则即使配置了不透明的appearance,iOS15下overCurrentContext模式依然可能出现透明问题 - iOS15新增了
compactScrollEdgeAppearance属性,对应横屏等紧凑尺寸场景下的滚动边缘导航栏样式,不配置会导致横屏时复现透明问题 - 不要在present之前直接修改picker的navigationBar样式,会被系统内部生命周期方法覆盖
内容的提问来源于stack exchange,提问作者Fadielse
相关产品推荐
相关产品推荐

