SwiftUI:如何消除Sheet关闭时的延迟?
解决Sheet关闭时视图偏移恢复延迟的问题
问题原因
你遇到的延迟是因为SwiftUI的sheet绑定的isPresented状态,会在Sheet的关闭动画完全结束后才更新为false。这就导致视图偏移的动画要等到Sheet完全消失后才开始,视觉上产生了延迟。
解决方案1:iOS 16.4+ 用PresentationPhase同步动画
iOS 16.4及以上提供了PresentationPhase枚举,它会在Sheet开始呈现/关闭时就更新状态(而非等待动画结束),可以完美同步偏移动画和Sheet的过渡动画:
import SwiftUI struct FirstView: View { // 使用PresentationPhase替代Bool状态 @State private var presentationPhase: PresentationPhase = .dismissed var body: some View { Text("Hello, World!") .onTapGesture { presentationPhase = .presenting } .sheet(isPresented: $presentationPhase.isPresented) { LaggyView() .presentationDetents([.medium, .large]) } // 当Sheet处于呈现中或已呈现状态时,保持偏移 .offset(y: presentationPhase == .presenting || presentationPhase == .presented ? -300 : 0) // 绑定动画到PresentationPhase的变化 .animation(.default, value: presentationPhase) } } struct LaggyView: View { var body: some View { Text("hello") } }
解决方案2:兼容旧版本的UIKit代理监听
如果需要支持iOS 16.4以下版本,可以通过UIKit的UIPresentationControllerDelegate监听Sheet开始关闭的事件,提前触发偏移恢复动画:
import SwiftUI import UIKit // 监听Sheet关闭开始的协调器 class SheetDismissalCoordinator: NSObject, UIAdaptivePresentationControllerDelegate { private let onDismissStarted: () -> Void init(onDismissStarted: @escaping () -> Void) { self.onDismissStarted = onDismissStarted } // Sheet开始关闭时触发 func presentationControllerWillDismiss(_ presentationController: UIPresentationController) { onDismissStarted() } } // UIViewControllerRepresentable用于注入代理 struct SheetDismissalListener: UIViewControllerRepresentable { let onDismissStarted: () -> Void func makeUIViewController(context: Context) -> UIViewController { let vc = UIViewController() vc.presentationController?.delegate = context.coordinator return vc } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} func makeCoordinator() -> SheetDismissalCoordinator { SheetDismissalCoordinator(onDismissStarted: onDismissStarted) } } struct FirstView: View { @State var showSheet: Bool = false @State var offsetY: CGFloat = 0 var body: some View { Text("Hello, World!") .onTapGesture { // 弹出Sheet时同步触发偏移动画 withAnimation(.default) { offsetY = -300 showSheet = true } } .sheet(isPresented: $showSheet, onDismiss: { // 确保Sheet完全关闭后偏移归位(防止拖拽中断动画) offsetY = 0 }) { LaggyView() .presentationDetents([.medium, .large]) // 注入监听视图,监听Sheet关闭开始事件 .background(SheetDismissalListener { withAnimation(.default) { offsetY = 0 } }) } .offset(y: offsetY) } } struct LaggyView: View { var body: some View { Text("hello") } }
两种方案都能让视图偏移的动画和Sheet的过渡动画同步进行,彻底消除关闭时的延迟。
内容的提问来源于stack exchange,提问作者b.joe
相关产品推荐
相关产品推荐

