SwiftUI View嵌入UIKit后Toolbar不显示问题求助
SwiftUI Toolbar在UIKit Popover中不显示的解决思路
问题描述
在SwiftUI中实现了RoomDetailsEditView,将其以Popover形式嵌入UIKit的RoomInformationViewController时,视图主体可正常显示,但包含取消、保存按钮的Toolbar完全不显示。
关键代码片段
SwiftUI视图实现
import SwiftUI import PhotosUI struct RoomDetailsEditView: View { @StateObject var viewModel: RoomDetailEditModel @State private var selectedItem: PhotosPickerItem? = nil @ToolbarContentBuilder private var toolbar: some ToolbarContent { ToolbarItem(placement: .cancellationAction) { Button("Abbrechen") { dismiss() } } ToolbarItem(placement: .confirmationAction) { Button("Speichern") { viewModel.saveProfileData() dismiss() }.disabled(!viewModel.viewGotEdited) } } private var topicSection: some View {...} private var avatar: some View {...} private var roomNameSection: some View {...} var body: some View { Form { avatar roomNameSection topicSection } .scrollDismissesKeyboard(.immediately) .navigationTitle("Raum bearbeiten") .navigationBarTitleDisplayMode(.inline) .toolbar { toolbar } } }
UIKit宿主ViewController
import Foundation import UIKit import SwiftUI class RoomInformationViewController: UIViewController { var collectionView: UICollectionView! = nil var dataSource: UICollectionViewDiffableDataSource<RoomInformationViewSections, RoomInformationElement>! = nil var room: Room! = nil override func viewDidLoad() { super.viewDidLoad() self.initNavigationBar() self.initCollectionView() self.loadDataSource() } ... @objc func editRoomData() { let viewModel = RoomDetailEditModel(room: self.room, isProfileEdit: false) let editRoomView = RoomDetailsEditView(viewModel: viewModel) let wrapperViewController = ViewControllerWrapper(rootView: editRoomView) let navController = UINavigationController(rootViewController: wrapperViewController) navController.modalPresentationStyle = .popover navController.modalTransitionStyle = .coverVertical self.present(navController, animated: true, completion: nil) } ... }
自定义视图包装器
class ViewControllerWrapper<Content: View>: UIViewController { private var hostingController: UIHostingController<Content>! init(rootView: Content) { super.init(nibName: nil, bundle: nil) hostingController = UIHostingController(rootView: rootView) } required init?(coder: NSCoder) { super.init(coder: coder) } override func viewDidLoad() { super.viewDidLoad() addChild(hostingController) view.addSubview(hostingController.view) hostingController.view.frame = view.bounds hostingController.didMove(toParent: self) print(hostingController.navigationItem.title) navigationController?.navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(title: "a", style: .plain, target: nil, action: nil) } }
问题根源
SwiftUI的.toolbar和.navigationTitle依赖自身的导航容器(NavigationStack/旧版NavigationView)或与UIKit导航系统的映射关系来渲染。当前的自定义ViewControllerWrapper阻断了UIHostingController的navigationItem与UIKitUINavigationController的关联,导致SwiftUI定义的Toolbar无法被UIKit导航栏识别。
修复方案
方案1:移除自定义Wrapper,直接使用UIHostingController
不需要额外的包装类,直接将SwiftUI视图交给UIHostingController,再传入UINavigationController:
@objc func editRoomData() { let viewModel = RoomDetailEditModel(room: self.room, isProfileEdit: false) let editRoomView = RoomDetailsEditView(viewModel: viewModel) // 直接实例化UIHostingController,跳过自定义Wrapper let hostingController = UIHostingController(rootView: editRoomView) let navController = UINavigationController(rootViewController: hostingController) navController.modalPresentationStyle = .popover navController.modalTransitionStyle = .coverVertical self.present(navController, animated: true, completion: nil) }
这种方式下,SwiftUI的.toolbar和.navigationTitle会自动映射到UIKit导航栏的对应元素,无需额外配置。
方案2:保留Wrapper,同步NavigationItem
如果必须保留自定义ViewControllerWrapper,需要将hostingController的navigationItem同步到Wrapper自身,让UIKit导航栏能获取到SwiftUI定义的内容:
class ViewControllerWrapper<Content: View>: UIViewController { private var hostingController: UIHostingController<Content>! init(rootView: Content) { super.init(nibName: nil, bundle: nil) hostingController = UIHostingController(rootView: rootView) // 同步hostingController的navigationItem到当前Wrapper self.navigationItem = hostingController.navigationItem } required init?(coder: NSCoder) { super.init(coder: coder) } override func viewDidLoad() { super.viewDidLoad() addChild(hostingController) view.addSubview(hostingController.view) // 添加入局自动适配,避免屏幕尺寸变化时布局错乱 hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] hostingController.didMove(toParent: self) // 移除多余的backBarButtonItem设置,避免干扰SwiftUI Toolbar渲染 } }
额外注意事项
- 若使用SwiftUI 4+,确保
.toolbar的placement参数(.cancellationAction、.confirmationAction)能正确映射到UIKit导航栏的对应位置; - 测试Popover的尺寸适配,确保Toolbar和内容区域都能完整显示。
内容的提问来源于stack exchange,提问作者Patrick Kuro
相关产品推荐
相关产品推荐

