will and way

ただの自分用メモを人に伝える形式で書くことでわかりやすくまとめてるはずのブログ

Lottieことはじめ

Lottieとは

Airbnb謹製のアニメーションツールでAfterEffectsでexportしたアニメーションをiOS、Androidで再生できるというすぐれものです。

f:id:matsuokah:20171014111823g:plain

上記のGIFはlottie-iosより転載

僕自身はこういうアニメーションはあまり好きではないのですが、
味気なさがなくなりますね!

ということで、シンプルなハンバーガーアイコンのスイッチを例に使い始めるまでの流れをまとめてみました。

1. Carthageで組み込み

github "airbnb/lottie-ios" "master"

cartfileに上記を記述してcarthage update --platform iOSして、プロジェクトに組み込むだけですね。

2. スイッチを組み込む

LOTAnimatedSwitchというクラスがあるのでそれを使います。

LOTAnimatedSwitch.hをみるとスイッチを作成するクラスメソッドが用意されてるのでこちらを使います。

/// Convenience method to initialize a control from the Main Bundle by name
+ (instancetype _Nonnull)switchNamed:(NSString * _Nonnull)toggleName;

/// Convenience method to initialize a control from the specified bundle by name
+ (instancetype _Nonnull)switchNamed:(NSString * _Nonnull)toggleName inBundle:(NSBundle * _Nonnull)bundle;

toggleNameにはアニメーションを記述したjsonファイルを指定する必要があります。 LottieではLottieFilesといって、
アニメーションをクリエイティブ・コモンズライセンスで公開しているストアがありますのでそこからダウンロードしてきます。

今回はハンバーガーアイコンをダウンロードしてきます。

ダウンロードされたzipを解答するとAfterEffectsのプロジェクトファイルと、Export済みのjsonがはいっているのでjsonをプロジェクトに組み込みます

f:id:matsuokah:20171014114015p:plain

let animatedSwitch = LOTAnimatedSwitch.init(named: "Hamburger")

これで読み込めるようになりました。

3. スイッチのアニメーションの範囲を決める

このままではアニメーションはうまく動きません。なぜならスイッチのON/OFFに対してのアニメーションの対応付をしていないからです。

スイッチはoff -> onのアニメーションとon -> offのアニメーションがあります

f:id:matsuokah:20171014114656p:plain

この間ですね。

LOTAnimatedSwitch.hをみるとアニメーションの範囲を割合で設定するメソッドが用意されています

- (void)setProgressRangeForOnState:(CGFloat)fromProgress
                        toProgress:(CGFloat)toProgress NS_SWIFT_NAME(setProgressRangeForOnState(fromProgress:toProgress:));
- (void)setProgressRangeForOffState:(CGFloat)fromProgress
                         toProgress:(CGFloat)toProgress NS_SWIFT_NAME(setProgressRangeForOffState(fromProgress:toProgress:));

先ほどダウンロードしたHamburger.jsonではoff->on->offということでonに戻るまでのアニメーションが記述されています。

アニメーションの進捗割合に対応付けると

off -> on : 0 -> 0.5
on -> off : 0.5 -> 1.0

と表すことができます。

したがって、setProgressの記述は以下のようになります。

animatedSwitch.setProgressRangeForOnState(fromProgress: 0, toProgress: 0.5)
animatedSwitch.setProgressRangeForOffState(fromProgress: 0.5, toProgress: 1.0)

この進捗割合はアニメーションの元ファイルに依存します。

off -> on : 0 -> 1.0
on -> off : 1.0 -> 0

で表せる場合もあるでしょう。

ということで、アニメーションの対応付が完了し、スイッチの作成ができました。

※レイアウトのコードは本筋から外れるので記載していません

4. 動かしてみる

f:id:matsuokah:20171014120502g:plain

タップしてるのですがわかりづらいですね(汗)

ということで、アニメーションの組み込みができました。

5. InterfaceBuilderで組み込めるようにしてみる

InterfaceBuilderで必要な要素だけを設定したらいい感じに動いてほしいです。
毎度、アニメーションの対応付のコードを書くのは面倒です。
ということで、InterfaceBuilderで設定できるようにします。

LottieSwitchView.swift

@IBDesignable
@IBInspectable

を使って、Interface Builderでアニメーションを定義できるようにします

import UIKit
import Lottie


@IBDesignable
class LottieSwitchView: UIView {
    @IBInspectable
    var filename: String = ""

    @IBInspectable
    var fromProgressToOn: CGFloat {
        set(newValue) {
            _fromProgressToOn = LottieSwitchView.shrinkInZeroToOne(value: newValue)
        }
        get {
            return _fromProgressToOn
        }
    }
    @IBInspectable
    var toProgressToOn: CGFloat {
        set(newValue) {
            _toProgressToOn = LottieSwitchView.shrinkInZeroToOne(value: newValue)
        }
        get {
            return _toProgressToOn
        }
    }

    @IBInspectable
    var fromProgressToOff: CGFloat {
        set(newValue) {
            _fromProgressToOff = LottieSwitchView.shrinkInZeroToOne(value: newValue)
        }
        get {
            return _fromProgressToOff
        }
    }
    @IBInspectable
    var toProgressToOff: CGFloat {
        set(newValue) {
            _toProgressToOff = LottieSwitchView.shrinkInZeroToOne(value: newValue)
        }
        get {
            return _toProgressToOff
        }
    }
    
    //// actual value
    private var _fromProgressToOn: CGFloat = 0
    private var _toProgressToOn: CGFloat = 0.5
    private var _fromProgressToOff: CGFloat = 0.5
    private var _toProgressToOff: CGFloat = 1.0

    override func awakeFromNib() {
        super.awakeFromNib()
        let animatedSwitch = LOTAnimatedSwitch.init(named: filename)
        animatedSwitch.setProgressRangeForOffState(fromProgress: fromProgressToOff, toProgress: toProgressToOff)
        animatedSwitch.setProgressRangeForOnState(fromProgress: fromProgressToOn, toProgress: toProgressToOn)
        self.addSubview(animatedSwitch)
        animatedSwitch.fitToParent()
    }
}

private extension LottieSwitchView {
    static func shrinkInZeroToOne(value: CGFloat) -> CGFloat {
        return min(1.0, max(value, 0))
    }
}

f:id:matsuokah:20171014123342p:plain

これで、InterfaceBuilderでファイル名、アニメーションの範囲を指定できるようになりました

InterfaceBuilder上ではUIViewで枠だけを作っていて
awakeFromNibで内部的にLOTAnimatedSwitchを作ってaddSubviewしています。

fitToParentは親Viewと同じframeになるようにConstraintsを設定しているだけです。

以上、Lottie事始めでした。

リポジトリ

github.com

次は、「AfterEffectsからアニメーションのJSONをExportする」記事を書こうと思います
↓書きました

blog.matsuokah.jp