iOSエンジニアのつぶやき

毎朝8:30に iOS 関連の技術について1つぶやいています。まれに釣りについてつぶやく可能性があります。

【Swift】UIView.animate で連続したアニメーションをする際のコールバック地獄をどうにかしたい

今回は SwiftUIView.animate で連続したアニメーションを実行する際のコールバック地獄を解消する方法を紹介したいと思います👀

結論

今回の悩みを解消できる素晴らしい記事があったので、車輪の再発明はしません✨

qiita.com

Swift の Variadic Parameters を使って、複数のアニメーションブロックを再帰関数で実行していく感じです。ここで定義した Extension method を使うことで、あのコールバック地獄から抜け出せます🦅

extension UIView {
    public static func animate(eachBlockDuration duration: TimeInterval, eachBlockDelay delay: TimeInterval = 0, eachBlockOptions options: UIView.AnimationOptions = .curveEaseInOut, animationBlocks: (() -> Void)..., completion: ((_ finished: Bool) -> Void)? = nil) {
        let isFinished = animationBlocks.isEmpty
        if isFinished {
            completion?(isFinished)
        } else {
            let animationArraySlice = ArraySlice(animationBlocks)
            UIView.animate(eachBlockDuration: duration, eachBlockDelay: delay, eachBlockOptions: options, animationArraySlice: animationArraySlice, completion: completion)
        }
    }

    private static func animate(eachBlockDuration duration: TimeInterval, eachBlockDelay delay: TimeInterval, eachBlockOptions options: UIView.AnimationOptions, animationArraySlice: ArraySlice<() -> Void>, completion: ((_ finished: Bool) -> Void)?) {
        let animation = animationArraySlice.startItem

        UIView.animate(withDuration: duration, delay: delay, options: options, animations: animation) { (finished) in
            let remainedAnimations = animationArraySlice.dropFirst()
            if remainedAnimations.isEmpty {
                completion?(finished)
            } else {
                UIView.animate(eachBlockDuration: duration, eachBlockDelay: delay, eachBlockOptions: options, animationArraySlice: remainedAnimations, completion: completion)
            }
        }
    }
}

使う時はこんな感じ

        UIView.animate(eachBlockDuration: 0.3, eachBlockDelay: 0.3, eachBlockOptions: .curveEaseOut,
            animationBlocks: {[weak self] in
                self?.view.alpha = 1.0
            }, {[weak self] in
                self?.view.alpha = 0.0
            },
            completion: {[weak self] finished in
                // TODO: Completion handle.
            })
    }

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com