如何判断SwiftUI中Sheet弹出动画的完成时机
SwiftUI中监听Sheet弹出动画完成时机的方法
在SwiftUI中,原生sheet修饰符没有直接提供弹出动画完成的回调,你原来使用的withAnimation(completion:)只会在show状态变更(比如从true变为false)的动画完成时触发,和Sheet系统动画的完成时机并不同步。要准确监听Sheet弹出动画的完成,最可靠的方式是借助UIKit的UIPresentationControllerDelegate代理方法。
方法一:封装带回调的自定义Sheet组件
通过UIViewControllerRepresentable包装UIKit控制器,利用代理捕捉动画完成事件:
import SwiftUI struct CompletionSheet<Content: View>: UIViewControllerRepresentable { let content: Content let onPresented: () -> Void let onDismissed: () -> Void func makeUIViewController(context: Context) -> UIHostingController<Content> { let controller = UIHostingController(rootView: content) // 设置代理监听动画完成事件 controller.presentationController?.delegate = context.coordinator return controller } func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(onPresented: onPresented, onDismissed: onDismissed) } // 实现UIPresentationControllerDelegate class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate { let onPresented: () -> Void let onDismissed: () -> Void init(onPresented: @escaping () -> Void, onDismissed: @escaping () -> Void) { self.onPresented = onPresented self.onDismissed = onDismissed } // Sheet弹出动画完成时触发 func presentationControllerDidPresent(_ presentationController: UIPresentationController) { onPresented() } // Sheet消失动画完成时触发 func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { onDismissed() } } }
使用方式
直接在原生sheet的内容参数中传入这个自定义组件:
struct SwiftUIView29: View { @State var show = false var body: some View { Button("show") { show.toggle() } .sheet(isPresented: $show) { CompletionSheet( content: Text("SheetView"), onPresented: { print("Sheet弹出动画完成!") }, onDismissed: { print("Sheet消失动画完成!") } ) } } }
方法二:封装成ViewModifier,简化调用
如果需要多次使用,可以封装成ViewModifier,让调用逻辑更贴近原生sheet:
extension View { func sheet<Content: View>( isPresented: Binding<Bool>, @ViewBuilder content: @escaping () -> Content, onPresented: @escaping () -> Void, onDismissed: @escaping () -> Void ) -> some View { self.modifier( SheetCompletionModifier( isPresented: isPresented, content: content(), onPresented: onPresented, onDismissed: onDismissed ) ) } } struct SheetCompletionModifier<Content: View>: ViewModifier { @Binding var isPresented: Bool let content: Content let onPresented: () -> Void let onDismissed: () -> Void func body(content: Content) -> some View { content .sheet(isPresented: $isPresented) { CompletionSheet( content: self.content, onPresented: onPresented, onDismissed: onDismissed ) } } }
使用方式
现在可以像调用原生sheet一样传入回调:
struct SwiftUIView29: View { @State var show = false var body: some View { Button("show") { show.toggle() } .sheet( isPresented: $show, content: { Text("SheetView") }, onPresented: { print("Sheet弹出完成") }, onDismissed: { print("Sheet消失完成") } ) } }
为什么原生withAnimation(completion:)不行?
你原来的代码中,withAnimation的completion闭包是针对show状态变更的动画,但Sheet的弹出/消失动画是系统内部独立管理的,和状态变更动画不同步。只有当show从true变为false时,Sheet的消失动画刚好和状态变更的动画周期重合,才会触发completion;而弹出时,状态变更没有动画(或动画瞬间完成),所以completion会立即执行,无法对应Sheet的弹出动画完成时机。
内容的提问来源于stack exchange,提问作者Amirca
相关产品推荐
相关产品推荐

