LoginSignup
1
1

More than 3 years have passed since last update.

ScrollView のスクロール中だけ Lottie アニメーションを止めたい! (Swift)

Posted at

はじめに

Lottie アニメーションを UIScrollView がスクロールしている最中のみ動かしたいときがあったので、その実装を紹介します。
具体的に言うと、

  • スクロール中: アニメーション停止
  • スクロール終了: アニメーション再開

という要求です。

準備: Lottie を動かす ViewController

ScrollView の動作関係なしに、ただ Lottie を動かす ViewController のコードを紹介します。
今回はこのコードを元に実装してみます。

コメント多めに書いてますので、ある程度は追えるかと思います。
今回はなんとなくランダムな位置に複数の Lottie アニメーションを配置してみました。

import UIKit
import Lottie

class ViewController: UIViewController {

    // MARK: - IBOutlet
    // scrollView の子要素で、 Lottie オブジェクトが配置される UIView
    @IBOutlet weak var contentView: UIView!

    // MARK: - Private properties
    var animations = [AnimationView]()

    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        // ランダムな位置に 3 個の Lottie オブジェクトを生成・配置・再生する
        // 複数配置していることに特に意味はありません。
        for _ in 1...3 {
            let x = CGFloat.random(in: 1.0...5.0) * 200.0
            let y = CGFloat.random(in: 1.0...6.0) * 100.0
            let animation = generateAnimation(x: x, y: y)
            animations.append(animation)
            contentView.addSubview(animation)
            animation.play()
        }
    }

    // MARK: - Private methods
    // 引数で受け取った座標を持つ Lottie オブジェクトを生成するメソッド
    // see: https://qiita.com/ngo275/items/c9e94bad7a7afc85e4f4
    private func generateAnimation(x: CGFloat, y: CGFloat) -> AnimationView {
        let animationView = AnimationView(name: "sample_animation")
        animationView.frame = CGRect(x: x, y: y, width: view.bounds.width * 0.2, height: view.bounds.height * 0.2)
        animationView.loopMode = .loop
        animationView.contentMode = .scaleAspectFit
        animationView.animationSpeed = 1

        return animationView
    }
}

スクロール中だけアニメーションを止めてみよう

このままでは viewDidLoad ライフサイクルで play() されっぱなしで、止めようがありません。
今回の要求として、「スクロール中はアニメーションを止める」というのがありますので、まずは UIScrollView を Outlet 接続しましょう。

    // MARK: - IBOutlet
    // scrollView の子要素で、 Lottie オブジェクトが配置される UIView
    @IBOutlet weak var contentView: UIView!
    @IBOutlet weak var backgroundScrollView: UIScrollView! // 追加

次に、追加した backgroundScrollView の delegate 先を self つまり ViewController 自身に設定します。

    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        backgroundScrollView.delegate = self // 追加

これで ScrollView の delegate メソッドが使えるようになりました。
UIScrollView の delegate メソッドは沢山ありますが、今回は

  • スクロールのし初め
  • スクロールの終わり

を検知することで

  • スクロールのし初め: アニメーションを一時停止
  • スクロールの終わり: アニメーションを再開

という風にして実装していきます。
沢山ある delegate メソッドの解説は 公式ドキュメント他の方がまとめてくださった記事 に譲って割愛します。
今回使うのは、以下の 2 つです。

    // 1.
    // 指が画面に触れ、スクロールが開始した瞬間に呼ばれるメソッド
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView)

    // 2.
    // 指が画面から離れ、慣性のスクロールが完全に止まった瞬間に呼ばれるメソッド
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)

これらのメソッドを使えば、以下のようにして、簡単にスクロール中にアニメーションを止めて、スクロールが終わったらアニメーションを再開するということが可能になります。
ちなみに、 stop() メソッドを使用していますが、 pause() メソッドでも大丈夫です。
アニメーションが最後まで行って止まるか、 pause() された瞬間に止まるかの違いです。

    // 指が画面に触れ、スクロールが開始した瞬間に呼ばれるメソッド
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        print(#function) // どの関数が呼ばれているか確認用に表示
        // スクロール開始と同時にアニメーションをストップ
        animations.forEach { $0.stop() }
    }

    // 指が画面から離れ、慣性のスクロールが完全に止まった瞬間に呼ばれるメソッド
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        print(#function) // どの関数が呼ばれているか確認用に表示
        // スクロール終了と同時にアニメーションをスタート
        animations.forEach { $0.play() }
    }

あとは、これを実装して終わりです。
ViewController に UIScrollViewDelegate を継承させます。
僕は extension して書いていますが、 class ViewController: UIViewController, UIScrollViewDelegate としても何も問題ありません。

// MARK: - UIScrollViewDelegate
extension ViewController: UIScrollViewDelegate {
    // 指が画面に触れ、スクロールが開始した瞬間に呼ばれるメソッド
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        print(#function) // どの関数が呼ばれているか確認用に表示
        // スクロール開始と同時にアニメーションをストップ
        animations.forEach { $0.stop() }
    }

    // 指が画面から離れ、慣性のスクロールが完全に止まった瞬間に呼ばれるメソッド
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        print(#function) // どの関数が呼ばれているか確認用に表示
        // スクロール終了と同時にアニメーションをスタート
        animations.forEach { $0.play() }
    }
}

これでビルドしてみると、スクロールしている間だけアニメーションが止まることを確認できるはずです。
最後に、完成した ViewController を載せます。


import UIKit
import Lottie

class ViewController: UIViewController {

    // MARK: - IBOutlet
    // scrollView の子要素で、 Lottie オブジェクトが配置される UIView
    @IBOutlet weak var contentView: UIView!
    @IBOutlet weak var backgroundScrollView: UIScrollView!

    // MARK: - Private properties
    var animations = [AnimationView]()

    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        backgroundScrollView.delegate = self

        // ランダムな位置に 3 個の Lottie オブジェクトを生成・配置・再生する
        // 複数配置していることに特に意味はありません。
        for _ in 1...3 {
            let x = CGFloat.random(in: 1.0...5.0) * 200.0
            let y = CGFloat.random(in: 1.0...6.0) * 100.0
            let animation = generateAnimation(x: x, y: y)
            animations.append(animation)
            contentView.addSubview(animation)
            animation.play()
        }
    }

    // MARK: - Private methods
    // 引数で受け取った座標を持つ Lottie オブジェクトを生成するメソッド
    // see: https://qiita.com/ngo275/items/c9e94bad7a7afc85e4f4
    private func generateAnimation(x: CGFloat, y: CGFloat) -> AnimationView {
        let animationView = AnimationView(name: "sample_animation")
        animationView.frame = CGRect(x: x, y: y, width: view.bounds.width * 0.2, height: view.bounds.height * 0.2)
        animationView.loopMode = .loop
        animationView.contentMode = .scaleAspectFit
        animationView.animationSpeed = 1

        return animationView
    }
}

// MARK: - UIScrollViewDelegate
extension ViewController: UIScrollViewDelegate {
    // 指が画面に触れ、スクロールが開始した瞬間に呼ばれるメソッド
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        print(#function) // どの関数が呼ばれているか確認用に表示
        // スクロール開始と同時にアニメーションをストップ
        animations.forEach { $0.stop() }
    }

    // 指が画面から離れ、慣性のスクロールが完全に止まった瞬間に呼ばれるメソッド
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        print(#function) // どの関数が呼ばれているか確認用に表示
        // スクロール終了と同時にアニメーションをスタート
        animations.forEach { $0.play() }
    }
}

参考文献

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1