You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何移除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 07:46:05