SwiftUI中实现fullScreenCover下层视图可交互的方法
纯SwiftUI实现全局顶层可交互动画视图
方法1:自定义可穿透的全屏覆盖(封装少量UIKit逻辑)
虽然用到了UIKit的窗口操作,但已封装为纯SwiftUI可用的组件,无需直接操作SceneDelegate:
- 创建全局状态管理器,控制动画视图的显示/隐藏
import SwiftUI class GlobalOverlayManager: ObservableObject { @Published var showAnimatedView = false var animatedContent: AnyView? } // 将管理器注入SwiftUI环境 struct GlobalOverlayManagerKey: EnvironmentKey { static let defaultValue = GlobalOverlayManager() } extension EnvironmentValues { var globalOverlayManager: GlobalOverlayManager { get { self[GlobalOverlayManagerKey.self] } set { self[GlobalOverlayManagerKey.self] = newValue } } }
- 封装顶层视图容器,自动将内容添加到系统最上层窗口
struct GlobalOverlayContainer: UIViewRepresentable { @Environment(\.globalOverlayManager) var manager func makeUIView(context: Context) -> UIView { let view = UIView() view.isUserInteractionEnabled = false // 允许点击穿透到下层内容 DispatchQueue.main.async { updateOverlay(in: view) } return view } func updateUIView(_ uiView: UIView, context: Context) { updateOverlay(in: uiView) } private func updateOverlay(in uiView: UIView) { // 清理旧内容 uiView.subviews.forEach { $0.removeFromSuperview() } guard manager.showAnimatedView, let content = manager.animatedContent else { return } // 包装SwiftUI视图为UIKit容器 let hostingController = UIHostingController(rootView: content) hostingController.view.backgroundColor = .clear hostingController.view.isUserInteractionEnabled = false // 动画视图本身不拦截交互 // 添加到最顶层窗口 if let window = UIApplication.shared.windows.last { window.addSubview(hostingController.view) hostingController.view.frame = window.bounds // 自动隐藏动画(可根据需求调整时长) DispatchQueue.main.asyncAfter(deadline: .now() + 2) { withAnimation { manager.showAnimatedView = false } } } } }
- 在App根视图中注入管理器并添加容器
@main struct YourApp: App { @StateObject private var overlayManager = GlobalOverlayManager() var body: some Scene { WindowGroup { ContentView() .environment(\.globalOverlayManager, overlayManager) .background(GlobalOverlayContainer()) } } }
- 在任意视图中调用显示动画
struct ContentView: View { @Environment(\.globalOverlayManager) var overlayManager @State private var showSheet = false var body: some View { VStack(spacing: 20) { Button("显示顶层动画") { overlayManager.animatedContent = AnyView( Text("我是顶层动画") .font(.largeTitle) .foregroundColor(.white) .padding() .background(.red) .cornerRadius(10) ) withAnimation { overlayManager.showAnimatedView = true } } Button("弹出测试Sheet") { showSheet = true } .sheet(isPresented: $showSheet) { Text("这是一个Sheet") .padding() } } .padding() } }
方法2:修正fullScreenCover的交互问题(仅iOS 16+可用)
如果坚持使用fullScreenCover,可尝试以下修饰符组合(部分场景可能存在系统限制):
.fullScreenCover(isPresented: $showAnimatedView) { Text("顶层动画") .font(.largeTitle) .foregroundColor(.white) .padding() .background(.red) .cornerRadius(10) .background(.clear) .allowsHitTesting(false) .presentationBackground(.clear) .presentationBackgroundInteraction(.enabled) .presentationDetents([.large]) // 必须设置detent才能启用backgroundInteraction }
注意:此方法受系统版本和场景限制,可靠性不如方法1。
内容的提问来源于stack exchange,提问作者Kudit
相关产品推荐
相关产品推荐

