如何移除UISheetPresentationController使用时出现的阴影?
解决UISheetPresentationController透明背景下拉时的额外阴影问题
你遇到的阴影是UISheetPresentationController内部私有视图_UISheetBackgroundView自带的,它不属于你创建的UIHostingController或容器视图控制器,所以直接修改sheetController的layer属性无效。以下是可行的解决方案:
方案:弹窗展示后移除系统背景视图的阴影
利用UISheetPresentationControllerDelegate的回调方法,在弹窗完全展示后,遍历容器视图的子视图,找到系统自带的背景视图并关闭阴影。
修改后的核心代码
更新你的Coordinator类,添加presentationControllerDidPresent方法:
class Coordinator: NSObject, UISheetPresentationControllerDelegate { @Binding var isPresented: Bool let onDismiss: (() -> Void)? init(isPresented: Binding<Bool>, onDismiss: (() -> Void)? = nil) { self._isPresented = isPresented self.onDismiss = onDismiss } func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { isPresented = false onDismiss?() } // 添加此方法移除阴影 func presentationControllerDidPresent(_ presentationController: UIPresentationController) { // 遍历容器视图子视图,定位系统sheet背景视图 for subview in presentationController.containerView.subviews { if NSStringFromClass(subview.classForCoder).contains("_UISheetBackgroundView") { subview.layer.shadowOpacity = 0 break } } } }
额外优化:避免重复创建视图控制器
你的updateUIView方法每次都会新建UIViewController,可能导致重复弹窗或内存问题。可以把容器VC存在Coordinator中,仅在首次展示时创建:
struct SheetPresentationForSwiftUI<Content>: UIViewRepresentable where Content: View { @Binding var isPresented: Bool let onDismiss: (() -> Void)? let detents: [UISheetPresentationController.Detent] let content: Content // 原有init代码不变... func makeUIView(context: Context) -> UIView { UIView() } func updateUIView(_ uiView: UIView, context: Context) { // 复用已创建的VC,无则新建 let viewController: UIViewController if let existingVC = context.coordinator.containerVC { viewController = existingVC // 更新内容视图 if let hostingVC = viewController.children.first as? UIHostingController<Content> { hostingVC.rootView = content } } else { viewController = UIViewController() let hostingController = UIHostingController(rootView: content) viewController.addChild(hostingController) viewController.view.addSubview(hostingController.view) hostingController.view.backgroundColor = .clear hostingController.view.translatesAutoresizingMaskIntoConstraints = false viewController.view.backgroundColor = .clear viewController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: viewController.view.topAnchor), hostingController.view.leadingAnchor.constraint(equalTo: viewController.view.leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: viewController.view.trailingAnchor), hostingController.view.bottomAnchor.constraint(equalTo: viewController.view.bottomAnchor) ]) hostingController.didMove(toParent: viewController) context.coordinator.containerVC = viewController } if let sheetController = viewController.presentationController as? UISheetPresentationController { sheetController.detents = detents sheetController.largestUndimmedDetentIdentifier = .large sheetController.prefersGrabberVisible = true sheetController.prefersScrollingExpandsWhenScrolledToEdge = false sheetController.prefersEdgeAttachedInCompactHeight = true sheetController.widthFollowsPreferredContentSizeWhenEdgeAttached = true } viewController.presentationController?.delegate = context.coordinator // 控制弹窗展示/关闭,避免重复操作 if isPresented, viewController.presentingViewController == nil { uiView.window?.rootViewController?.present(viewController, animated: true) } else if !isPresented, viewController.presentingViewController != nil { viewController.dismiss(animated: true) } } func makeCoordinator() -> Coordinator { Coordinator(isPresented: $isPresented, onDismiss: onDismiss) } class Coordinator: NSObject, UISheetPresentationControllerDelegate { @Binding var isPresented: Bool let onDismiss: (() -> Void)? // 存储容器VC var containerVC: UIViewController? init(isPresented: Binding<Bool>, onDismiss: (() -> Void)? = nil) { self._isPresented = isPresented self.onDismiss = onDismiss } func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { isPresented = false onDismiss?() // 弹窗关闭后清空引用,避免下次复用旧实例 containerVC = nil } func presentationControllerDidPresent(_ presentationController: UIPresentationController) { for subview in presentationController.containerView.subviews { if NSStringFromClass(subview.classForCoder).contains("_UISheetBackgroundView") { subview.layer.shadowOpacity = 0 break } } } } }
原理说明
系统的UISheetPresentationController会在containerView中添加私有视图_UISheetBackgroundView,负责绘制sheet的背景和阴影。通过在弹窗展示完成后找到该视图并设置shadowOpacity = 0,即可彻底移除下拉时出现的额外阴影。
注:虽然使用了私有类名,但实际开发中该方案稳定性较高,苹果极少修改此私有类的命名。若后续系统版本有变动,可通过视图的layer属性(如shadowPath、shadowColor)定位目标视图。
内容的提问来源于stack exchange,提问作者user1969
相关产品推荐
相关产品推荐

