Search
💣

애니메이션이 끝난 시점 알아오기

애니메이션 콜백 메서드가 없는 이유?

“다른 방식으로 애니메이션이 끝난 시점을 알아올 수 있어서가 아닐까?” - 비비
struct ViewTransition: View { @State var show = false var body: some View { ZStack { if !show { Text("View transition") .onDisappear { //show 는 즉시 토글됨. 하지만 // RoundedRectangle 의 애니메이션이 끝났을 때 호출 // if else 문으로 엮여 있는 View 들의 라이프 사이클이 함께 가져가진다. } } else { RoundedRectangle(cornerRadius: 30) .fill(.blue) .padding() .transition(.scale(scale: 0.1, anchor: .bottomTrailing)) .zIndex(1) } } .onTapGesture { withAnimation(.spring()) { show.toggle() } } } }
Swift
복사
toggle 값 → 즉시
Appear → 즉시
disappear → 애니메이션이 끝난 시점.

1. View extension 적용

extension View { /// Calls the completion handler whenever an animation on the given value completes. /// - Parameters: /// - value: The value to observe for animations. /// - completion: The completion callback to call once the animation completes. /// - Returns: A modified `View` instance with the observer attached. func onAnimationCompleted<Value: VectorArithmetic>(for value: Value, completion: @escaping () -> Void) -> ModifiedContent<Self, AnimationCompletionObserverModifier<Value>> { return modifier(AnimationCompletionObserverModifier(observedValue: value, completion: completion)) } } /// An animatable modifier that is used for observing animations for a given animatable value. struct AnimationCompletionObserverModifier<Value>: AnimatableModifier where Value: VectorArithmetic { /// While animating, SwiftUI changes the old input value to the new target value using this property. This value is set to the old value until the animation completes. var animatableData: Value { didSet { notifyCompletionIfFinished() } } /// The target value for which we're observing. This value is directly set once the animation starts. During animation, `animatableData` will hold the oldValue and is only updated to the target value once the animation completes. private var targetValue: Value /// The completion callback which is called once the animation completes. private var completion: () -> Void init(observedValue: Value, completion: @escaping () -> Void) { self.completion = completion self.animatableData = observedValue targetValue = observedValue } /// Verifies whether the current animation is finished and calls the completion callback if true. private func notifyCompletionIfFinished() { guard animatableData == targetValue else { return } /// Dispatching is needed to take the next runloop for the completion callback. /// This prevents errors like "Modifying state during view update, this will cause undefined behavior." DispatchQueue.main.async { self.completion() } } func body(content: Content) -> some View { /// We're not really modifying the view so we can directly return the original input value. return content } }
Swift
복사

2. 적절한 곳에 사용

struct IntroductionView: View { @State private var introTextOpacity = 0.0 var body: some View { VStack { Text("Welcome to SwiftLee") .opacity(introTextOpacity) .onAnimationCompleted(for: introTextOpacity) { print("Intro text animated in!") } }.onAppear(perform: { withAnimation(.easeIn(duration: 1.0)) { introTextOpacity = 1.0 } }) } }
Swift
복사