iOS 16.4之前版本SwiftUI中实现sheet背景透明的替代方案
iOS 16.4 之前实现 Sheet 背景透明的替代方案
在iOS 16.4之前,SwiftUI原生的.sheet没有直接设置背景透明的API,想要实现相同效果,主要有两种可行方案:
方案一:基于UIKit包装系统Sheet(保留原生特性)
通过UIViewControllerRepresentable包装UIKit的UISheetPresentationController,可以实现背景透明同时保留系统Sheet的拖拽调整、拖拽关闭等原生特性。
实现代码
首先定义包装器:
struct TransparentSheet<Content: View>: UIViewControllerRepresentable { @Binding var isPresented: Bool let content: Content let detents: [UISheetPresentationController.Detent] func makeUIViewController(context: Context) -> UIHostingController<Content> { let hostingController = UIHostingController(rootView: content) hostingController.view.backgroundColor = .clear return hostingController } func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) { if isPresented, uiViewController.presentingViewController == nil { let sheet = uiViewController.sheetPresentationController sheet?.detents = detents sheet?.largestUndimmedDetentIdentifier = .medium // 可选:设置该尺寸下背景不暗化 sheet?.prefersGrabberVisible = true // 可选:显示顶部拖拽条 // 关键配置:确保视图和背景透明 uiViewController.view.backgroundColor = .clear uiViewController.presentationController?.delegate = context.coordinator } else if !isPresented, uiViewController.presentingViewController != nil { uiViewController.dismiss(animated: true) } } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate { let parent: TransparentSheet init(_ parent: TransparentSheet) { self.parent = parent } func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { parent.isPresented = false } // 移除系统默认的背景暗化和容器背景 func presentationController(_ presentationController: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? { let vc = UIViewController() vc.view.backgroundColor = .clear return vc } } }
使用方式
给View添加扩展方便调用:
extension View { func transparentSheet<Content: View>(isPresented: Binding<Bool>, detents: [UISheetPresentationController.Detent], @ViewBuilder content: @escaping () -> Content) -> some View { self.background(TransparentSheet(isPresented: isPresented, content: content(), detents: detents)) } }
在页面中使用:
struct ContentView: View { @State private var showFormView = false var body: some View { Button("打开透明Sheet") { showFormView = true } .transparentSheet(isPresented: $showFormView, detents: [.medium(), .large()]) { FormView() .background(Color.clear) // 确保内容视图本身背景透明 } } }
方案二:纯SwiftUI自定义模态视图(灵活易实现)
如果不需要系统Sheet的原生交互特性,完全可以用SwiftUI的ZStack、动画和手势自己实现一个背景透明的模态视图,代码更简洁,也更灵活。
实现代码
struct ContentView: View { @State private var showFormView = false var body: some View { ZStack { Button("打开自定义透明模态") { showFormView = true } if showFormView { // 可选:背景遮罩,设为.clear则完全透明 Color.black.opacity(0.3) .ignoresSafeArea() .onTapGesture { showFormView = false } FormView() .frame(maxWidth: .infinity) .background(Color.clear) .cornerRadius(16) .padding(.horizontal) .transition(.move(edge: .bottom)) .offset(y: showFormView ? 0 : UIScreen.main.bounds.height) } } .animation(.easeInOut, value: showFormView) } }
方案对比
- 方案一:保留系统Sheet的所有原生交互(拖拽调整尺寸、拖拽关闭等),但需要编写UIKit相关代码,复杂度稍高。
- 方案二:纯SwiftUI实现,代码简单易维护,但需要自己实现拖拽、尺寸调整等交互,适合需求简单的场景。
内容的提问来源于stack exchange,提问作者fs_tigre
相关产品推荐
相关产品推荐

