SwiftUI で数値の増減をアニメーションさせる


数字がどんどん増えていくような演出をしたいとき、
普通に書くだけだと思った通りのアニメーションをしてくれません。
そこで、徐々に変化させながら表示するためのコードを用意してみました。

コード例

ContentView.swift
import SwiftUI
struct ContentView: View {

    // 変化する値 count
    @State var count: Int = 0
    var body: some View {

        // 変化する値 count を読み取る
        AnimationReader(count) { value in
            // count ではなく value の方を使って表示する
            Text("total: \(value)")
        }

        // 画面が表示された後に数字を増やしてみるテスト
        .onAppear {
            withAnimation(.easeInOut(duration: 10)) {
                count += 99
            }
        }
    }
}

AnimationReader.swift

  • コピペして, どこかにおいてください
AnimationReader.swift
fileprivate struct AnimationReaderModifier<Body: View>: AnimatableModifier {
    let content: (CGFloat) -> Body
    var animatableData: CGFloat

    init(value: CGFloat, @ViewBuilder content: @escaping (CGFloat) -> Body) {
        self.animatableData = value
        self.content = content
    }

    func body(content: Content) -> Body {
        self.content(animatableData)
    }
}

struct AnimationReader<Content: View>: View {

    let value: CGFloat
    let content: (_ animatingValue: CGFloat) -> Content

    init(_ observedValue: Int, @ViewBuilder content: @escaping (_ animatingValue: Int) -> Content) {
        self.value = CGFloat(observedValue)
        self.content = { value in content(Int(value)) }
    }

    init(_ observedValue: CGFloat, @ViewBuilder content: @escaping (_ animatingValue: CGFloat) -> Content) {
        self.value = observedValue
        self.content = content
    }

    var body: some View {
        EmptyView()
            .modifier(AnimationReaderModifier(value: value, content: content))
    }
}

お役に立てましたらぜひ LGTM を!