SwiftUI/UIKit实现底部Sheet非Large档位回落效果的方法
实现底部Sheet非Large档位的回落效果(SwiftUI & UIKit)
SwiftUI 实现方案
原生SwiftUI的Sheet默认仅.large档位支持拖拽回落,要在其他档位实现该效果,需借助UIKit的UISheetPresentationController代理自定义 dismissal 逻辑:
- 创建代理类处理回落逻辑
class SheetDelegate: NSObject, UISheetPresentationControllerDelegate { func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) { guard let sheetController = presentationController as? UISheetPresentationController else { return } guard let currentDetent = sheetController.selectedDetentIdentifier else { return } // 非large档位时触发回落,这里以回落至medium为例 if currentDetent != .large { sheetController.animateChanges { sheetController.selectedDetentIdentifier = .medium } } else { // large档位正常关闭Sheet presentationController.presentingViewController.dismiss(animated: true) } } }
- 在SwiftUI视图中注入代理
struct SheetProxy: UIViewControllerRepresentable { let delegate: SheetDelegate func makeUIViewController(context: Context) -> UIViewController { let vc = UIViewController() vc.modalPresentationStyle = .pageSheet vc.sheetPresentationController?.delegate = delegate return vc } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } struct ContentView: View { @State private var showing = false private let sheetDelegate = SheetDelegate() var body: some View { Button("Toggle") { showing.toggle() } .sheet(isPresented: $showing) { Text("Test sheet") .presentationDetents([.medium, .large]) .background(SheetProxy(delegate: sheetDelegate)) } } }
UIKit 实现方案
直接基于UISheetPresentationController配置档位并实现代理:
class ViewController: UIViewController { private let sheetDelegate = SheetDelegate() @IBAction func toggleSheet(_ sender: UIButton) { let sheetVC = UIViewController() sheetVC.view.backgroundColor = .systemBackground sheetVC.modalPresentationStyle = .pageSheet if let sheetController = sheetVC.sheetPresentationController { sheetController.detents = [.medium(), .large()] sheetController.delegate = sheetDelegate } present(sheetVC, animated: true) } } // 复用之前的SheetDelegate类 class SheetDelegate: NSObject, UISheetPresentationControllerDelegate { func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) { guard let sheetController = presentationController as? UISheetPresentationController else { return } guard let currentDetent = sheetController.selectedDetentIdentifier else { return } if currentDetent != .large { sheetController.animateChanges { sheetController.selectedDetentIdentifier = .medium } } else { presentationController.presentingViewController.dismiss(animated: true) } } }
补充说明
- 若需自定义档位(如固定高度的Sheet),可使用
UISheetPresentationController.Detent.custom()创建,在代理中匹配对应identifier即可调整回落目标。 - 回落动画通过系统
animateChanges方法触发,与原生Sheet动画保持一致。
内容的提问来源于stack exchange,提问作者Gamekohl
相关产品推荐
相关产品推荐

