Altconf marin cover

Power Up Your Animations! 💫

シンプルなアニメーションを作る方法は、皆さん誰もが知っています。ですが、とても信じられないほど美しいアニメーションを作りたいと思ったらどうしますか? raywenderlich.comのチームメンバーであるMarin Todorovが、レイヤーやアニメーションライブラリについて詳しくなればどんなことを達成できるのかを明らかにしてくれます。このプレゼンテーションに魔法は含まれません——ただパワフルで、印象的なアニメーションだけがあります!


この講演のサンプルコードは GitHubにあります。

iOS 9の新機能 (0:35)

iOS 9では、アニメーションのための新機能はそこまで多くありません。

新しい点のひとつとしては、UIViewAnimationOptionsがOptionSetTypeになりました。今までは、ビットマスクによって組み合わせることのできる単なる値でしたが、今回からは、すべて集合の要素となりました。そのため、集合の形で組み合わせることができます。iOS 8では、ビューアニメーションに何もオプションを与えたくない場合、nilを指定する必要がありましたが、空集合を宣言するための角括弧を指定するだけでよいのです。

iOS 9では、角括弧とともにRepeatとCurveLinearといったオプションを組み合わせることもできます。もしひとつのオプションのみで良い場合は、ちょっと奇妙に見えますが、角括弧は要りません。そのオプションが集合の正しい要素であれば、自動的に判断され正常に処理されます。

// iOS 8
UIView.animateWithDuration(1.0, delay: 0.0, options: nil, animations {
  v.center.y += 100.0
}, completion: nil)

// iOS 9
UIView.animateWithDuration(1.0, delay: 0.0, options: [], animations {
  v.center.y += 100.0
}, completion: nil)

もうひとつの新しい点は、スプリングアニメーションにあります。

iOS 9では低レベルのAPIとしてCASpringAnimationをレイヤーに対して用いることができます。今までもこのクラスはiOS内にありましたが、プライベートクラスでした。実は、内部で今までのあなたのスプリングアニメーションを担っていたのはこのクラスだったのです。今年Appleは我々にこれを開放してくれたため、オープンソースのスプリングアニメーション実装を使うことを止められます。

もし今までにスプリングアニメーションをあまり使ったことがない場合、スプリングアニメーションには質量(mass)、スプリングの硬さ(stiffness)、粘性(damping)を指定する必要があることを知っておいてください。一度これらの値を指定した後は、settlingDurationを取得することができます。これは読み取り専用のプロパティで、そのアニメーションが何秒で落ち着くかを伝えてくれます。

// iOS 8
UIView.animateWithDuration(1.0, delay: 0.0,
    usingSpringWithDamping: 0.25, initialSpringVelocity: 0.12,
    options: [], animations: {
        v.center.x += 100.0
    }, completion: nil)

// iOS 9
class CASpringAnimation : CABasicAnimation {

 var mass: CGFloat
 var stiffness: CGFloat
 var damping: CGFloat

 var initialVelocity: CGFloat

 var settlingDuration: CFTimeInterval { get }
}

イントロダクション (4:46)

iOS 7で、私たちはアプリを新しいビジュアルスタイルに移行しました。しかし、もしアプリがそれと認識されるようなグラフィカルスタイルでない場合、それがインディーズの開発者によるものか、Appleによるものかを判断する方法はありません。カラフルなテキストでアプリの色合いを変えることはできますが、本当に大きな違いを見せたいのであれば、アニメーションを加える必要があります。

記事の更新情報を受け取る

アニメーションは、あなたのブランドおよびスタイルについての全てを伝える際に、大きな違いを生み出します。これは良いことですが——みなさん誰もが、いくつかのアニメーションを作る方法を知っています。

ビューであれば、赤い四角形を数ポイント右に動かすだけのようなシンプルなアニメーションなど。(もしあなたがSwiftを使っていない場合、そろそろ使うべきです。)問題は、私が思うに、多くの人が図形が画面を横切る、あるいはフェードイン・アウトするようなアニメーションのみで終わりにしてしまうことです。

もしあなたがより複雑なことをやりたい、と思ったときはStack Overflowにアクセスして、具体的な回答に辿り着くことができるでしょう。これで、アニメーションを作成することはできます。しかし、それ以外のことをしたいときには、Stack Overflowにまた別の質問を投稿する必要があります。

実験 🔥 (7:20)

アニメーションへのアプローチの最も良い方法は実験することだと私は考えます。

Core Animationは、特にUIKitでも、あなたのアプリの中でアニメーションを作るのに使うことができるとても多くの機能を提示しています。私はこれを非常に効果的で素晴らしいものだと考えています。

今から、あなたにできる3つのことをご紹介します。まず、いろいろなプロパティを実験してみましょう。次に、新しいレイヤーをつかってみましょう。最後に、アニメーションの期間を設定する以上の、枠にとらわれない考えで奇抜なことをやってみましょう。この講演では、私はEasyAnimationを使います。これはUIKitに2、3の機能を追加するだけのライブラリです。

デモ (8:23)

このデモ内では、Easy Animationがどんなことができるのか2、3の例を紹介したいと思います。

ここにプラスボタンが画面上に置かれているアプリがあります。おそらく、これが押されたときには何かしらのアニメーションが追加されるのでしょう。

ユーザーインタフェースにおけるアニメーションは通常、ユーザーに今何かが起こっているというフィードバックを提示するために使われるものです。ですので私は、ここではボタンが押された時にボタンをバウンスさせたいと思います。

それにはEasy AnimationのUIView.animateAndChainWithDurationを用います。Easy Animationは、すでにUIKitにて提供されているものにぴったりな2, 3のメソッドを追加するものです。このメソッドは順序だったアニメーションの作成を非常に簡単にしてくれるため、様々な実験を素早く行うことができます。

delayを0.25にし、CurveEaseOutを使ってみます。これはアニメーションの効果の効き具合を、その終わりに向かって遅くするものです。青い正方形が今回のボタンです。ここでは単純に、少し縮小するよう変形をかけます。Easy Animationのおかげで、最初のアニメーションが終わった後に、簡単に次のアニメーションを連鎖して開始することができます。変形のあとは、少し回転・拡大するようにスプリングアニメーションを設定します。

UIView.animateAndChainWithDuration(0.25, delay: 0.0, options: .CurveEaseOut, animations: {
            self.blueSquare.transform = CGAffineTransformMakeScale(0.8, 0.8)
        }, completion: nil).animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.33, initialSpringVelocity: 0.0, options: nil, animations: {

            self.blueSquare.transform = CGAffineTransformConcat(
                CGAffineTransformMakeScale(1.33, 1.33),
                CGAffineTransformMakeRotation(CGFloat(-M_PI_2))
            )
        }, completion: nil)

1. プロパティの実験 (11:47)

私は先ほど、まずプロパティの実験をしよう、と言いましたね。CALayerには、いろいろ実験できる多くのプロパティがあります。そのプロパティについては全てドキュメントの中で説明されています。

このプロパティの中には、アニメーションを作る際に使えることがわかりづらいcornerRadiusのようなプロパティもあります。ですがそういったプロパティもなかなか格好いいことをしてくれます。

デモ: CALayer (12:18)

では、cornerRadiusをアニメーションさせ、アニメーションチェーンのふたつ目で50.0にセットします。

最初は0.0にしておきます。毎回アニメーションが走るたび、また正方形に戻ります。ゼリーのかたまりのような動きは、これらのプロパティの組み合わせによって作ることができます。

説明としては2点あります。スプリングアニメーションを使っていることと、角を丸くするのと回転させるのを同時に行っていることです。このようにして、本当に格好いいアニメーションを作ることができました。さらに境界線もアニメーションさせることができます。ここでは境界線をつけてから、単に取り除くことで、エッチングのような効果をつけるようにしました。

UIView.animateAndChainWithDuration(0.25, delay: 0.0, options: .CurveEaseOut, animations: {
            self.blueSquare.transform = CGAffineTransformMakeScale(0.8, 0.8)
            self.blueSquare.layer.cornerRadius = 0.0
            self.blueSquare.layer.borderWidth = 5.0

        }, completion: nil).animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.33, initialSpringVelocity: 0.0, options: nil, animations: {

            self.blueSquare.transform = CGAffineTransformConcat(
                CGAffineTransformMakeScale(1.33, 1.33),
                CGAffineTransformMakeRotation(CGFloat(-M_PI_2))
            )
            self.blueSquare.layer.cornerRadius = 50.0
            self.blueSquare.layer.borderWidth = 0.0

        }, completion: nil)

デモ: CAShapeLayer (14:49)

さらに奇抜なレイヤーのプロパティを見てみましょう。

CAShapeLayerでは、lineDashPatternとlineDashPhaseによりmarching antsを作ることができます。

例えば写真内に選択範囲を作ったとすると、その選択範囲は破線の長方形で表されますね。そこで、この例では切り取りツールを作っているとしましょう。選択範囲はCAShapeLayerで作り、lineDashPatternを設定することができます。lineDashPatternは破線のうち何ピクセルを描いて何ピクセル間隔が空くかを指定することができます。

さらに、lineDashPhaseを用いることによってアニメーションさせることができます。これはCore Annimationにそのパターンをいつ描画し始めるかを伝えるものです。

override func viewWillAppear(animated: Bool) {
    ...

    view.layer.addSublayer(selection)

    //configure the dash pattern
    selection.lineDashPattern = [5, 3]
}

override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
    ...

    //let the ants march!
    UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveLinear | .Repeat, animations: {
        self.selection.lineDashPhase = 8.0
        }, completion: nil)

}

2. レイヤーの実験 (17:43)

2つ目にすることは、レイヤーを使って実験することです。単なるCALayerやCAShapeLayer以外にも、レイヤーにはいくつもの種類があります。そして、それらの多くはアニメーションできるような特別なプロパティを有しています。私のお気に入りはCAReplicatorLayerです。

デモ: CAReplicatorLayer (17:59)

CAReplicatorLayer は基本的にはコンテナとなるレイヤーで、何かレイヤーを入れた場合、それのコピーをつくってくれます。画面にCAReplicatorLayerを追加し、そこにレイヤーを入れれば、そのレイヤーは画面上に表示されます。この例では、複製される、単なるドットを表すレイヤーをひとつ作り、スケールするようにアニメーションさせます。

//add a replicator layer
let r = CAReplicatorLayer()
r.frame = view.bounds
view.layer.addSublayer(r)

//make a simple dot layer
let dot = CALayer()
dot.bounds = CGRect(x: 0.0, y: 0.0, width: 5.0, height: 5.0)
dot.position = CGPoint(x: 18.0, y: view.center.y)
dot.backgroundColor = UIColor.greenColor().CGColor
dot.borderColor = UIColor(white: 1.0, alpha: 1.0).CGColor
dot.borderWidth = 1.0
dot.cornerRadius = 2.0

r.addSublayer(dot)

UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseOut | .Repeat | .Autoreverse, animations: {
    dot.transform = CATransform3DMakeScale(1.4, 10, 1.0)
    }, completion: nil)

この動いているドットを見た時、わたしはSiriとその正弦波を連想しました。ですので、CAReplicatorLayerを使ってもっとそう見えるようにしてみましょう。

レイヤーをReplicatorレイヤーに追加する際は、そのレイヤーをどうするかをReplicatorレイヤーに伝える必要があります。

まずは、ドットのコピーを35個ほしいと思います。それで、それぞれのコピーの間には時間の遅延を与えてみましょう。オリジナルのレイヤーに変更を加えると、Replicatorレイヤーが全てのコピーに対して同じことをしてくれます。

例えば、アニメーションのタイミングがコピーからコピーに、0.1秒の遅延で伝わっていくようにしてみます。つまり、あるコピーのアニメーションはひとつ前のコピーから0.1秒遅れて始まることになります。

最後に、instantTransformプロパティによって各コピーの位置を設定することができます。ここでは20ポイント右に移動させることにします。これにより、コピーは、ひとつ前のコピーの20ポイント右に配置されることになります。さらに、各コピーの中間の位置をアニメーションさせることができます。

r.instanceCount = 35
r.instanceDelay = 0.1
r.instanceTransform = CATransform3DMakeTranslation(20.0, 0.0, 0.0)

UIView.animateWithDuration(1.25, delay: 0.0, options: .Repeat | .Autoreverse, animations: {
    r.instanceTransform = CATransform3DMakeTranslation(10.0, 0.0, 0.0)
}, completion: nil)

デモ: テーブルビューのアニメーション (21:55)

私は友達とともにSkyscanner appを見ていました。これはフライトの検索結果を表示してくれるものです。知りたい到着地を指定すれば、そのフライトの時刻表を見ることができます。その中からひとつをクリックすると、新しい画面がプッシュされて、前の画面と同じフライトの結果が画面の一番上に、その下に詳細が表示されます。

このように複数のビューコントローラがある場合、画面上でそれらの遷移をアニメーションさせるべきです。今の場合だと最初のビューが左に行き、他のビューが右からスライドしてきましたね。私はさらに格好のいいアニメーションとして、選択された行のスナップショットを取り、遷移にのみ使うビューを作り、それをスクリーンに加えるようにしました。Skyscannerは同じことをやっています。

フライトを選択すると、そのフライトを示す行はスクリーンに残り、画面の一番上に移動し、その後に詳細が背後にスライドしてきます。まず、テーブルビューからセルを取得し、snapshotViewAfterScreenUpdatesを呼ぶことで、セルのコピーを新しくUIViewとして得ることができます。そうしたら、それをトランジションコンテナビューに追加し、フェードアウト、その後削除するようにします。

3. 何でも実験しよう (24:28)

最後の例は、何でも実験しよう、ということを伝えることにあります。何でもとは、聞いたこともないようなブログに目を通す、あるいはAppleのドキュメントを見よう、という意味です。私が見つけたのは、本当に深くに埋まっていたもので、実際にiPhone上で動作させるのに少し時間がかかりました。

これは、写真を含む投稿のリストがあり、タップされた時にカスタムビューコントローラトランジションを使い、奇抜な方法で写真の表示・非表示を行う例です。

Core Imageは画像の遷移をCore Image transition filtersを使って行う方法を提供しています。

マスクを与えることで、ひとつの画像から他の画像へのトランジションをさせることができます。ですので、マスクを用いるどんなカスタムフィルタでも作ることができます。マスクはグレーの画像であれば何でも構いません。トランジションではまず黒い部分が表示され、次に灰色の部分、そして最後に白い部分が表示されます。

全てのフィルター適用済みのフレームを作るために、常にフィルターをかける必要があります。私が作ったマスクはこちらにあります。

ありがとうございました。

翻訳:岩谷 明 Akira Iwaya 校正・校閲:本村美絵 Mie Hommura

About the content

This talk was delivered live in June 2015 at AltConf. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.

Marin Todorov

Marin TodorovはiOSコンサルタントで著者でもあります。代表的な著書に「iOS Animations by Tutorials」があり、現在は「iOS Animations by Emails」というニュースレターを配信しています。20年前にApple ][のソフトウェア開発を始めてから現在に至ります。この間、Monster TechnologiesやNative Instrumentsといった、すばらしい企業で働き、4つの国を渡り歩きました。raywenderlich.comの設立メンバーの一人であり、コードだけでなく、ブログや本を書いたり、講演や教育にも関わっています。ときどき、自分の書いたコードをOSSとして公開もしています。現在はサンティエゴに住んでいます。

4 design patterns for a RESTless mobile integration »

close