CatHand Blog

アプリ開発やMac弄り

SwiftUIでフレームアニメーション

UIImage.animatedImage(with: , duration:) で作った UIImage を Image(uiImage: ) に渡してもアニメーションしてくれません。

UIImageViewUIViewRepresentable 経由で使ってもいいんですが、 Timer で簡易的にフレームアニメーションするやつを書いてみました。

import Combine
import SwiftUI

struct AnimatedImage<V: View>: View {

    @State private var index = 0

    let count: Int
    let content: (Int) -> V
    private var timer: LoadingTimer

    init(count: Int, timeInterval: TimeInterval, content: @escaping (Int) -> V) {
        self.count = count
        self.timer = LoadingTimer(timeInterval: timeInterval)
        self.content = content
    }
    
    var body: some View {
        content(index)
            .onReceive(timer.publisher, perform: { _ in
                if self.index + 1 > self.count - 1 {
                    self.index = 0
                } else {
                    self.index += 1
                }
            })
            .onAppear {
                self.timer.start()
            }
            .onDisappear {
                self.timer.cancel()
            }
    }
}

class LoadingTimer {

    let publisher: Timer.TimerPublisher
    private var timerCancellable: Cancellable?

    init(timeInterval: TimeInterval) {
        publisher = Timer.publish(every: timeInterval, on: .main, in: .default)
    }
    
    func start() {
        timerCancellable = publisher.connect()
    }

    func cancel() {
        timerCancellable?.cancel()
    }
}

Timer で contents を更新しているだけですね。

使い方は

var images: [UIImage]
...
AnimatedImage(count: images.count, timeInterval: 1) {
                    Image(uiImage: images[$0])
                        .resizable()
                        .scaledToFill()
                }

みたいなかんじです。

作ってみて思ったけど、フレームアニメーションというかスライドショー的なやつですね。

GIFアニメとかを動かしたいときは、SDWebImage の SwiftUI 版があるのでそちらを使うと良いと思います。

github.com