SwiftUI如何实现将自定义View覆盖在包括sheet在内的所有视图上层
问题原因
你当前的实现是把顶栏视图挂载在ContentView的NavigationView层级上,而通过present方式弹出的视图会被系统渲染在更高的UIWindow层级上,自然会盖住你原来的顶栏视图。
解决方案
直接将顶栏视图挂载到当前App的最顶层UIWindow上,完全脱离业务视图的层级限制,不管是push还是present的场景都能正常显示在最上层。
完整实现代码如下:
import SwiftUI import UIKit class TopViewManager: ObservableObject { static let shared = TopViewManager() @Published var isShowing: Bool = false private init() {} // 全局调用显示顶栏 func show() { isShowing = true addTopViewToWindow() } // 全局调用隐藏顶栏 func hide() { isShowing = false removeTopViewFromWindow() } private var topHostingVC: UIHostingController<AnyView>? private func addTopViewToWindow() { // 获取当前活跃的KeyWindow guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }), topHostingVC == nil else { return } let topView = TopNotificationView(isShowing: $isShowing) let hostingVC = UIHostingController(rootView: AnyView(topView)) hostingVC.view.backgroundColor = .clear hostingVC.view.frame = window.bounds hostingVC.view.isUserInteractionEnabled = true window.addSubview(hostingVC.view) topHostingVC = hostingVC } private func removeTopViewFromWindow() { topHostingVC?.view.removeFromSuperview() topHostingVC = nil } } // 你的自定义顶栏视图 struct TopNotificationView: View { @Binding var isShowing: Bool var body: some View { GeometryReader { geometry in ZStack(alignment: .top) { Color.clear // 透明占位,不阻挡下层视图正常交互 HStack(alignment: .center) { Text("Test") } .frame(width: geometry.size.width - 44, height: 58) .background(Color.gray.opacity(0.7)) .foregroundColor(.white) .cornerRadius(20) .transition(.slide) .opacity(isShowing ? 1 : 0) .offset(y: isShowing ? 0 : -100) // 配合过渡动画效果更流畅 } .edgesIgnoringSafeArea(.all) } } }
使用方式
不需要在业务视图上挂载扩展,任意需要触发显示/隐藏的位置直接全局调用即可:
// 显示顶栏 TopViewManager.shared.show() // 隐藏顶栏 TopViewManager.shared.hide()
注意事项
- 如果你的App需要适配iPad多窗口/分屏场景,自行调整获取KeyWindow的逻辑即可
- 如果需要自定义顶栏内容,直接修改
TopNotificationView的实现即可,也可以在TopViewManager中增加参数传递自定义内容 - 如果不需要顶栏区域外的交互被阻挡,可以给hostingVC的view增加点击判断,点击区域不在顶栏范围内时直接透传给下层视图
内容的提问来源于stack exchange,提问作者Shivani Bajaj
相关产品推荐
相关产品推荐

