You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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正常显示:
    iOS17正常显示ToolbarItems

  • iOS18工具栏消失:
    iOS18未显示ToolbarItems

解决方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 21:35:09