iOS18中SwiftUI ToolbarItems不显示问题求助
iOS18中混合SwiftUI/UIKit场景下ToolbarItems不显示问题修复
问题场景
项目采用UIKit与SwiftUI混合开发:
BusStopsViewControllerRepresentable作为UIKit导航栈入口,内部包含BusStopsController- 触发跳转后,通过
UIHostingController加载BusStopDetailView,该视图内嵌BusStopDetailViewControllerRepresentable - 在
BusStopDetailView中用SwiftUI的.toolbar配置了标题视图和收藏按钮,这些元素在iOS17正常显示,iOS18下完全消失。
核心代码
// BusStopsViewControllerRepresentable struct BusStopsViewControllerRepresentable: UIViewControllerRepresentable { @EnvironmentObject var busStopsViewModel: BusStopsViewModel func makeUIViewController(context: Context) -> some UIViewController { let controller = BusStopsController(busStopsViewModel: busStopsViewModel) let navigationController = UINavigationController(rootViewController: controller) return navigationController } } class BusStopsController: UIViewController { // ... func annotationViewDidTap(busStopEntity: BusStopEntity) { let detailView = BusStopDetailView(busStop: busStopEntity).environmentObject(busStopsViewModel) let controller = UIHostingController(rootView: detailView) navigationController?.pushViewController(controller, animated: true) } } // BusStopDetailView struct BusStopDetailView: View { let busStop: BusStopEntity @EnvironmentObject var busStopsViewModel: BusStopsViewModel var body: some View { BusStopDetailViewControllerRepresentable(busStop: busStop) .ignoresSafeArea() .toolbar { // iOS18不显示,iOS17正常 ToolbarItem(placement: .principal) { BusStopTitleView(busStop: busStop) } ToolbarItem(placement: .primaryAction) { Button { busStopsViewModel.updateBusStopFavourite(busStop: busStop) } label: { Image(systemName: "star") } } } } } struct BusStopDetailViewControllerRepresentable: UIViewControllerRepresentable { let busStop: BusStopEntity func makeUIViewController(context: Context) -> BusStopDetailController { let controller = BusStopDetailController() controller.busStop = busStop return controller } }
效果对比
iOS17正常显示:

iOS18工具栏消失:

解决方案
iOS18调整了SwiftUI与UIKit导航栈的交互逻辑,SwiftUI的.toolbar无法自动关联到UIKit导航栏上下文。推荐两种修复方式:
方案1:在UIHostingController层面用UIKit API配置导航栏
直接在跳转时,给UIHostingController的navigationItem配置标题视图和右侧按钮,替代SwiftUI的.toolbar:
func annotationViewDidTap(busStopEntity: BusStopEntity) { let detailView = BusStopDetailView(busStop: busStopEntity).environmentObject(busStopsViewModel) let hostingController = UIHostingController(rootView: detailView) // 配置标题视图 let titleHostingController = UIHostingController(rootView: BusStopTitleView(busStop: busStopEntity)) titleHostingController.view.backgroundColor = .clear hostingController.navigationItem.titleView = titleHostingController.view // 配置收藏按钮 let favoriteButton = UIBarButtonItem( image: UIImage(systemName: "star"), style: .plain, target: self, action: #selector(toggleFavorite(_:)) ) favoriteButton.tag = busStopEntity.id // 用tag传递busStop标识 hostingController.navigationItem.rightBarButtonItem = favoriteButton navigationController?.pushViewController(hostingController, animated: true) } @objc private func toggleFavorite(_ sender: UIBarButtonItem) { guard let busStop = busStopsViewModel.busStops.first(where: { $0.id == sender.tag }) else { return } busStopsViewModel.updateBusStopFavourite(busStop: busStop) }
方案2:调整SwiftUI视图结构,确保Toolbar关联导航上下文
在BusStopDetailView中包裹一层NavigationStack(iOS16+),让SwiftUI的.toolbar识别到导航环境:
struct BusStopDetailView: View { let busStop: BusStopEntity @EnvironmentObject var busStopsViewModel: BusStopsViewModel var body: some View { NavigationStack { BusStopDetailViewControllerRepresentable(busStop: busStop) .ignoresSafeArea() .toolbar { ToolbarItem(placement: .principal) { BusStopTitleView(busStop: busStop) } ToolbarItem(placement: .primaryAction) { Button { busStopsViewModel.updateBusStopFavourite(busStop: busStop) } label: { Image(systemName: "star") } } } .navigationBarTitleDisplayMode(.inline) // 匹配标题显示模式 } } }
注:这种方式需处理UIKit与SwiftUI导航栈的兼容问题,可根据需求设置hostingController.navigationItem.hidesBackButton = true避免重复显示返回按钮。
总结
iOS18对混合栈的Toolbar处理逻辑更严格,优先推荐方案1(UIKit API配置),兼容性更稳定;方案2适合偏SwiftUI的架构,但需额外处理导航栈交互细节。
内容的提问来源于stack exchange,提问作者Koh
相关产品推荐
相关产品推荐

