[iOS][Swift] キーフレームアニメーションを作るRazzleDazzleの簡単な使い方

2016.05.11

今回はSwift版のkeyframeアニメーション作れるRazzleDazzleというライブラリの簡単な使い方をご紹介したいと思います。 RazzleDazzleはJazzHandsというOvjective-Cのキーフレームアニメーション作れるライブラリのSwfit版になります。

キーフレームアニメーションといっても、どんなことが出来るのかイメージがわかない方はGithubページのデモGIFを見ると判りやすいです。 https://github.com/IFTTT/RazzleDazzle

今回は基本的な使い方をマスターするために、下記のような簡単なウォークスルーページを作りたいと思います。

イメージ図

開発環境

また、今回の開発環境は下記で実施しています。

Xcode 7.3.1
Swift 2.2
CocoaPods 1.0.0

ターゲットはiOS8.1、デバイスはiPhone、画面回転無しでProjectを作成しました。

導入

CocoaPodsで追加します。

use_frameworks!

target "ターゲット名" do
  pod 'RazzleDazzle'
end

実装

対象のViewControllerに対して import RazzleDazzle を追加して、 UIViewControllerAnimatedPagingScrollViewController に変更します。

ViewController.swift

import UIKit
import RazzleDazzle

class ViewController: AnimatedPagingScrollViewController {

AnimatedPagingScrollViewControllerはウォークスルーなどでよく見かける横にスクロールするUIScrollViewが用意されています。

ページ数の設定

スクロールのページ数の設定をします。今回は3ページとしました。下記コードを追加します。

ViewController.swift

// ページ数
override func numberOfPages() -> Int {
    return 3
}

ページとアニメーションの関係

RazzleDazzleのアニメーションはUIScrollViewの位置で変化を定義します。画面の左上の位置が基準になります。 スクロールの位置 = タイムライン のイメージです。

タイムラインのイメージ

1ページ目を表示する時は0、2ページ目は1になります。0より大きく1未満の数字は1ページ目から2ページに遷移する途中の状態です。

左上の位置

背景色のアニメーション

まず最初に背景色を変更します。

ViewController.swift

    override func viewDidLoad() {
        super.viewDidLoad()

        configureAnimations()
    }

    // アニメーションを設定する
    private func configureAnimations() {
        configureScrollView()
    }
	
    private func configureScrollView() {
        // 背景色のアニメーション
        let backgroundColorAnimation = BackgroundColorAnimation(view: scrollView)
        backgroundColorAnimation[-1] = UIColor.greenColor()
        backgroundColorAnimation[0] = UIColor.grayColor()
        backgroundColorAnimation[1] = UIColor.yellowColor()
        backgroundColorAnimation[1.3] = UIColor.brownColor()
        backgroundColorAnimation[2] = UIColor.redColor()
        backgroundColorAnimation[3] = UIColor.blueColor()
        animator.addAnimation(backgroundColorAnimation)
    }

let backgroundColorAnimation = BackgroundColorAnimation(view: scrollView) で、scrollViewの背景アニメーションを作成します。
backgroundColorAnimation[0] = UIColor.grayColor() これはは0の位置の時に灰色にするという定義です。今回は各ページ毎に色を変えてますが、背景色を変更するタイミングだけ定義すればOKです。
animator.addAnimation(backgroundColorAnimation) で背景色の変更のアニメーションを登録します。

上記のサンプルコードの挙動は
1ページ目 :灰色
2ページ目 :黄色
3ページ目 :赤
2ページ目から3ページになる時に一旦茶色になってから変わる
1ページ目で左にスクロールしようとすると黄緑っぽい色になる
3ページ目で右にスクロールしようとすると青(紫?)っぽい色になる
となります。

sample

パーツの配置

コード上で画像など配置するコンポーネントを追加していきます。
今回はそれぞれのページにテキスト用とイメージ用の画像を用意しました。

画像

サンプルで利用している画像は下記からお借りしています。
http://flat-icon-design.com/

ViewController.swift

    private let page1ImageView = UIImageView(image: UIImage(named: "page1"))
    private let page2ImageView = UIImageView(image: UIImage(named: "page2"))
    private let page3ImageView = UIImageView(image: UIImage(named: "page3"))
    private let text1ImageView = UIImageView(image: UIImage(named: "text1"))
    private let text2ImageView = UIImageView(image: UIImage(named: "text2"))
    private let text3ImageView = UIImageView(image: UIImage(named: "text3"))

    // ページ数
    override func numberOfPages() -> Int {
        return 3
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        configureViews()
        configureAnimations()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // 画面にパーツを配置する
    private func configureViews () {
        contentView.addSubview(page1ImageView)
        contentView.addSubview(page2ImageView)
        contentView.addSubview(page3ImageView)
        contentView.addSubview(text1ImageView)
        contentView.addSubview(text2ImageView)
        contentView.addSubview(text3ImageView)
    }

contentView に対して addSubview します。
この状態で実行すると、下図のように最初のページに固まってしまうので、表示するページの設定と制約等をつけます。 ひどい状態

ページ指定と制約

ViewController.swift

    // アニメーションを設定する
    private func configureAnimations() {
        configureScrollView()
        configurePage1()
        configurePage2()
        configurePage3()
    }

    private func configurePage1() {
        contentView.addConstraint(NSLayoutConstraint(item: page1ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 0.65, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: text1ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.65, constant: 0))
        keepView(page1ImageView, onPage: 0)
        keepView(text1ImageView, onPage: 0)
    }

    private func configurePage2() {
        contentView.addConstraint(NSLayoutConstraint(item: page2ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 0.65, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: text2ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.65, constant: 0))
        keepView(page2ImageView, onPage: 1)
        keepView(text2ImageView, onPage: 1)
    }

    private func configurePage3() {
        contentView.addConstraint(NSLayoutConstraint(item: page3ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 0.65, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: text3ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.65, constant: 0))
        keepView(page3ImageView, onPage: 2)
        keepView(text3ImageView, onPage: 2)
    }

NSLayoutConstraintを使い位置を設定します。今回は上半分にイメージ画像、下半分にテキストの画像の簡単な構成なので、制約の内容は各ページ同じです。
keepView でどのページに表示するかを定義します。
keepView(page1ImageView, onPage: 0)page1ImageView を最初のページ(左上が0)に表示します。
上記コードを実行すると以下の様な感じになります。
配置したイメージ

※ 一部背景色がどぎつい感じだったので変更してます

keepView の onPageについて

onPageに設定した値は正確にはX座標の中心点になります。keepView(page1ImageView, onPage: 0)の 0の部分を0.5に変更すると、 次のページとの中間部分が中心点になります。

ページの途中になった状態

フェードのアニメーションを入れる

keepView(page1ImageView, onPage: 0)keepView(page1ImageView, onPages: [0, 1], atTimes: [0, 1]) に書き換えます。
onPagesは複数のページにまたがって表示したい時に配列で定義します。
atTimesはその配置するフレームを定義します。
上記のコードに変更して実行すると以下の挙動になります。
最初の画像の動きが変わった

2ページ目の時も1ページ目の画像が残るようになりました。
次に2ページ目になるにつれ、画像がフェードアウトになるようにアニメーションを追加します。

ViewController.swift

    private func configurePage1() {
        contentView.addConstraint(NSLayoutConstraint(item: page1ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 0.65, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: text1ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.65, constant: 0))
        keepView(page1ImageView, onPages: [0, 1], atTimes: [0, 1])
        keepView(text1ImageView, onPage: 0)

        let alphaAnimation = AlphaAnimation(view: page1ImageView)
        alphaAnimation[0] = 1
        alphaAnimation[1] = 0
        animator.addAnimation(alphaAnimation)
    }

AlphaAnimationを使うと透明度を変更することが出来ます。
alphaAnimation[0] = 1
上記コードは左上の位置が0(1ページ)の時はアルファ値が1、
alphaAnimation[1] = 0
左上の位置が1(2ページ)の時がアルファ値が0になるアニメーションになります。

同じように2ページめと3ページ目のイメージのアニメーションを修正します。
下記は今回の最終版のコードになります。

ViewController.swift

import UIKit
import RazzleDazzle

class ViewController: AnimatedPagingScrollViewController {

    private let page1ImageView = UIImageView(image: UIImage(named: "page1"))
    private let page2ImageView = UIImageView(image: UIImage(named: "page2"))
    private let page3ImageView = UIImageView(image: UIImage(named: "page3"))
    private let text1ImageView = UIImageView(image: UIImage(named: "text1"))
    private let text2ImageView = UIImageView(image: UIImage(named: "text2"))
    private let text3ImageView = UIImageView(image: UIImage(named: "text3"))

    // ページ数
    override func numberOfPages() -> Int {
        return 3
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        configureViews()
        configureAnimations()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // 画面にパーツを配置する
    private func configureViews () {
        contentView.addSubview(page1ImageView)
        contentView.addSubview(page2ImageView)
        contentView.addSubview(page3ImageView)
        contentView.addSubview(text1ImageView)
        contentView.addSubview(text2ImageView)
        contentView.addSubview(text3ImageView)
    }

    // アニメーションを設定する
    private func configureAnimations() {
        configureScrollView()
        configurePage1()
        configurePage2()
        configurePage3()
    }

    private func configureScrollView() {
        // 背景色のアニメーション
        let backgroundColorAnimation = BackgroundColorAnimation(view: scrollView)
        backgroundColorAnimation[-1] = UIColor.greenColor()
        backgroundColorAnimation[0] = UIColor.lightGrayColor()
        backgroundColorAnimation[1] = UIColor.yellowColor()
        backgroundColorAnimation[1.3] = UIColor.brownColor()
        backgroundColorAnimation[2] = UIColor.orangeColor()
        backgroundColorAnimation[3] = UIColor.blueColor()
        animator.addAnimation(backgroundColorAnimation)
    }

    private func configurePage1() {
        contentView.addConstraint(NSLayoutConstraint(item: page1ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 0.65, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: text1ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.65, constant: 0))
        keepView(page1ImageView, onPages: [0, 1], atTimes: [0, 1])
        keepView(text1ImageView, onPage: 0)

        let alphaAnimation = AlphaAnimation(view: page1ImageView)
        alphaAnimation[0] = 1
        alphaAnimation[1] = 0
        animator.addAnimation(alphaAnimation)
    }

    private func configurePage2() {
        contentView.addConstraint(NSLayoutConstraint(item: page2ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 0.65, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: text2ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.65, constant: 0))
        keepView(page2ImageView, onPages: [0, 1, 2], atTimes: [0, 1, 2])
        keepView(text2ImageView, onPage: 1)

        let alphaAnimation = AlphaAnimation(view: page2ImageView)
        alphaAnimation[0] = 0
        alphaAnimation[1] = 1
        alphaAnimation[2] = 0
        animator.addAnimation(alphaAnimation)
    }

    private func configurePage3() {
        contentView.addConstraint(NSLayoutConstraint(item: page3ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 0.65, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: text3ImageView, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.65, constant: 0))
        keepView(page3ImageView, onPages: [1, 2], atTimes: [1, 2])
        keepView(text3ImageView, onPage: 2)

        let alphaAnimation = AlphaAnimation(view: page3ImageView)
        alphaAnimation[1] = 0
        alphaAnimation[2] = 1
        animator.addAnimation(alphaAnimation)
    }
}

最終結果

さいごに

今回は単純にbackgroundColorとalphaしか使いませんでしたが、他にも色々なプロパティのアニメーションをすることが出来ます。 色々と試してみると楽しいかもしれません。