SwiftUI嵌入UIHostingController+UINavigation时,导航栏遮罩失效问题
将SwiftUI视图嵌入带有UINavigation的UIHostingController中,期望弹出.sheet浮层时,用半透明黑色遮罩屏幕剩余区域。已编写如下代码实现:
public var body: some View { ZStack { Color.color(for: ColorToken.Background._02, colorScheme: colorScheme) .ignoresSafeArea() mainContent .padding(.top) if store.bottomSheetPosition != .hidden { Color.black .opacity(0.6) .ignoresSafeArea() .transition(.opacity) .contentShape(Rectangle()) .onTapGesture { send(.didTapDismissBottomSheet, animation: .easeOut(duration: 0.3)) } } } }
但目前遇到问题:半透明黑色图层虽已覆盖屏幕剩余区域,但导航栏按钮仍处于视图层级顶层,未被遮罩且可点击,请问有哪些解决办法?
解决办法
方案1:在UIKit层级添加遮罩层
由于UINavigationBar属于UIKit的视图层级,SwiftUI的ZStack无法穿透覆盖它,直接在UIKit的根视图或当前控制器的父视图上添加遮罩:
- 弹窗显示时,创建一个
UIView,设置背景色为黑色、透明度0.6,添加到UIApplication.shared.keyWindow?.rootViewController?.view(或当前UIHostingController的view.superview)上,确保层级高于导航栏 - 给这个UIView添加点击手势,触发弹窗关闭逻辑
- 弹窗隐藏时,从父视图中移除该遮罩View
方案2:改用SwiftUI原生NavigationStack
如果项目架构允许,放弃UIKit的UINavigationController,使用SwiftUI原生的NavigationStack构建导航。此时导航栏属于SwiftUI层级,ZStack中的遮罩层可以自然覆盖导航栏按钮:
// 示例代码结构 NavigationStack { ZStack { Color.color(for: ColorToken.Background._02, colorScheme: colorScheme) .ignoresSafeArea() mainContent .padding(.top) if store.bottomSheetPosition != .hidden { Color.black .opacity(0.6) .ignoresSafeArea() .transition(.opacity) .contentShape(Rectangle()) .onTapGesture { send(.didTapDismissBottomSheet, animation: .easeOut(duration: 0.3)) } } } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("操作") {} } } }
方案3:动态控制导航栏按钮的交互状态
在弹窗显示/隐藏时,通过UIKit API修改导航栏按钮的可交互性:
- 扩展UIHostingController,添加方法获取导航栏的所有
UIBarButtonItem - 当
store.bottomSheetPosition变为非隐藏状态时,遍历导航栏的items,设置isEnabled = false,同时可调整按钮的alpha值为0.5,视觉上提示不可点击 - 弹窗隐藏时,恢复按钮的
isEnabled = true和alpha值
内容的提问来源于stack exchange,提问作者Sevenfortyseven
相关产品推荐
相关产品推荐

