iOS SwiftUI Animation Complete Listener

執行效果

重寫可動畫類數據

//VectorArithmetic 可動畫類型的數據
extension View {
    //定義Data為VectorArithmetic
    //ModifiedContent是指,將此Self示圖
    //經由AnimationDataChangeModifier,修改後產生新的示圖
    //modifier主要是回傳ModifiedContent
    
    func onAnimationDataChange(
        for data: Data,
        onComplete: @escaping () -> Void
    )
    -> ModifiedContent<Self, AnimationDataChangeModifier> {
        return modifier(AnimationDataChangeModifier(
                            changeData: data,
                            onComplete: onComplete))
    }
}

//指處where Data: VectorArithmetic
//確保資料類型是VectorArithmetic
//AnimatableModifier繼承於 
//Animatable, ViewModifier,可動畫的修改器
struct AnimationDataChangeModifier
: AnimatableModifier where Data: VectorArithmetic {
    //animatableData是繼承Animatable的資料,監聽變化
    var animatableData: Data {
        didSet {
            dataChangeCallback()
        }
    }

    private var previousData: Data
    private var onComplete: () -> Void
    //此處要注意,順序很重要
    init(
        changeData: Data,
        onComplete: @escaping () -> Void
    ) {
        //先註冊callback,完成時回調
        self.onComplete = onComplete
        
        //此處是關鍵
        //animatableData改變會先呼叫didSet
        //此時呼叫dataChangeCallback
        //animatableData已改變資料,previousData尚未改變
        self.animatableData = changeData
        
        //執行這行後previousData才改變資料
        previousData = changeData
    }

    private func dataChangeCallback() {
        guard animatableData == previousData else {
            return
        }

        DispatchQueue.main.async {
            self.onComplete()
        }
    }

    func body(content: Content) -> some View {
        return content
    }
}

使用方法

//onAnimationDataChange
//必須要使用withAnimation的變數變化,才會觸發
//因為VectorArithmetic 可動畫類型的數據
struct MainView: View {
    @State var opacity: Double = 1
    @State var animation = false
    var body: some View {
        Text("1234567890abcdefg")
            .opacity(animation ? opacity : 1)
            .animation(animation ?
                           Animation.linear : nil
            )
            .onAnimationDataChange(for: opacity) {
                if opacity == 0.1 {
                    withAnimation(
                        Animation.linear(duration: 1)) {
                        opacity = 1
                    }
                } else {
                    withAnimation(
                        Animation.linear(duration: 1)) {
                        opacity = 0.1
                    }
                }
            }
            .onAppear {
                animation = true
                withAnimation(
                    Animation.linear(duration: 1)) {
                    opacity = 0.1
                }

            }
    }
}

訂閱Codeilin的旅程,若有最新消息會通知。

廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

WordPress.com.

向上 ↑

%d 位部落客按了讚: