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

如何判断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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 19:43:22