UIBarButtonItem自定义视图在UIHostingController不生效问题
问题根源
UIHostingController内部会在视图加载完成后自动重写navigationItem的相关配置,如果你在控制器入栈前、视图未加载时就给navigationItem赋值,配置会被后续的内部逻辑覆盖,最终表现为按钮为nil、视图层级中无对应frame。普通UIViewController没有这套SwiftUI相关的导航配置接管逻辑,所以相同的赋值代码可以正常运行。
提示:作为
customView传入的自定义控件如果没有明确的尺寸(通过frame、AutoLayout约束或者intrinsicContentSize提供尺寸),即使赋值时机正确,UIBarButtonItem也会因为无法计算布局而无法显示。
可行修复方案
方案1:子类化UIHostingController,在
viewDidLoad生命周期内设置导航按钮
这是稳定性最高的UIKit兼容方案,此时控制器视图已经加载完成,后续不会被内部逻辑覆盖配置:final class ContentHostingController: UIHostingController<SomeView> { override func viewDidLoad() { super.viewDidLoad() let customButton = CustomButton() // 给自定义按钮设置明确尺寸,根据实际需求调整宽高 customButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44) navigationItem.leftBarButtonItem = UIBarButtonItem(customView: customButton) } } // 使用时替换原来的UIHostingController初始化逻辑 let controller = ContentHostingController(rootView: SomeView()) navigationController.setViewControllers([controller], animated: false)方案2:使用SwiftUI原生导航修饰符配置按钮
如果不需要严格依赖UIKit的UIBarButtonItem逻辑,直接走SwiftUI的导航配置可以完全避免双端配置冲突:struct SomeView: View { var body: some View { Text("测试页面") .navigationBarBackButtonHidden(true) .toolbar { ToolbarItem(placement: .navigationBarLeading) { CustomButton() } } } }方案3:入栈后主线程异步赋值(不推荐,仅适合快速调试场景)
不需要子类化控制器,但是极端场景下可能出现配置时机不对的问题:let controller = UIHostingController(rootView: SomeView()) navigationController.setViewControllers([controller], animated: false) DispatchQueue.main.async { let customButton = CustomButton() customButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44) controller.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: customButton) }
内容的提问来源于stack exchange,提问作者Tanya Marchuk
相关产品推荐
相关产品推荐

