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

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

https://qiita.com/ngo275/items/c9e94bad7a7afc85e4f4

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

## インストール

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

```ruby
# Podfile

pod 'lottie-ios'
```

## コード

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

```swift
// 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ファイルの名前を渡す。

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122365/b6915002-9ed6-e94c-15ad-191d3027ae78.gif)



```swift
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()
            }
        }
    }
}
```

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

![ezgif.com-gif-maker (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122365/eb152dff-8642-4b49-01f6-5542722f040c.gif)


```swift
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でも導入できてとても良い。