SwiftUIでLottieを使ってリッチなアニメーションを簡単に実現してみる


以下の記事を以前書いたが、最近SwiftUIを使ってLottieを利用したのでその方法について書いていく。

Lottieに関する説明はいろいろな記事でもされているのでここでは触れないで、SwiftUI上での利用方法について見ていく。

インストール

インストール方法はSwiftUIを使うからと言って特には変わらない。GitHubの説明に従う。CocoaPodsを利用する場合は、Podfileに以下を追加してpod installをターミナルで呼ぶだけ。コード内でimport LottieとするとLottieが利用可能になる。

# Podfile

pod 'lottie-ios'

コード

以下のようなファイルを作る。

// LottieView.swift

import SwiftUI
import Lottie

struct LottieView: UIViewRepresentable {
    private let tag = 100 // 必要があれば変えてください

    var name: String
    var loopMode: LottieLoopMode = .playOnce
    var animationView = AnimationView()
    @Binding var playing: Bool

    func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
        let view = UIView(frame: .zero)
        animationView.tag = tag
        animationView.animation = Animation.named(name)
        animationView.contentMode = .scaleAspectFit
        animationView.loopMode = loopMode

        animationView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(animationView)

        NSLayoutConstraint.activate([
            animationView.heightAnchor.constraint(equalTo: view.heightAnchor),
            animationView.widthAnchor.constraint(equalTo: view.widthAnchor)
        ])

        return view
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
        if playing {
            (uiView.viewWithTag(tag) as! AnimationView).play()
        } else {
            (uiView.viewWithTag(tag) as! AnimationView).stop()
        }
    }
}

使い方は以下のような感じ。ローディングビューのようなコンポーネントだと表示中は常に再生をしていたいので、loopModeには.loopを、playingには.constant(true)を渡す。nameにはアニメーションのJSONファイルの名前を渡す。

struct LoadingView: View {
    var body: some View {
        LottieView(name: "loading", loopMode: .loop, playing: .constant(true)).frame(width: 250, height: 250)
    }
}

struct SomeView: View {
    @State var loading: Bool

    var body: some View {        
        ZStack {
            // メインのビュー
            Text("Contents")

            // loadingがtrueの時のみLoadingViewを表示
            if loading {
                LoadingView()
            }
        }
    }
}

一方で、ユーザのインタラクションに応じて、再生状態を変えたいようなケースは以下のようになる。

struct PlayerView: View {
    @Binding var playing: Bool

    var body: some View {
        LottieView(name: "player", loopMode: .loop, playing: playing).frame(width: 250, height: 250)
    }
}

struct SomeView: View {
    @State var playing: Bool

    var buttonImage: String {
        playing ? "ic_playing" : "ic_play"
    }

    var body: some View {        
        ZStack {
            PlayerView(playing: $playing)
            Button(action: { playing.toggle() }) {
                Image(buttonImage)
            }
        }
    }
}

まとめ

Lottieがあればアニメーション周りはエンジニアとしては楽できるので非常に便利なツール。SwiftUIでも導入できてとても良い。