Animations
みなさんが iOS と他のモバイル OS との最大の違いについてユーザに尋ねると、アニメーションは間違いなくトップの答えのひとつとなるでしょう。フェード、フリップ、回転、バウンド、カールは Apple のアプリにありふれています。
これらのいくつかはデフォルトの UI 要素 (UINavigationController, UITableView など) で顕著です。そして本当に素晴らしいことは、それらはすべて同じ API に基づいて構築されているということです。文字通り、UIView はたった 1 行のコードでアニメーションできます。それは信じられないくらい素晴らしいことです。
さぁ、さっそくいくつか遊んでみましょう。画面の周りにボックスを移動させてみましょう。私を信じてください。コードは今述べた通りにシンプルです。
window にビューを追加することから始め、そして設定したポイントの周辺でアニメーションします。これは比較的に短いので、ここで AppDelegate のすべての変更を破棄しましょう。
def application(application, didFinishLaunchingWithOptions:launchOptions)
  @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame)
  @window.makeKeyAndVisible
  # the points we're going to animate to
  @points = [[0, 0], [50, 0], [0, 50], [50, 50]]
  @current_index = 0
  # usual method of adding views to our window
  @view = UIView.alloc.initWithFrame [@points[@current_index], [100, 100]]
  @view.backgroundColor = UIColor.blueColor
  @window.addSubview(@view)
  animate_to_next_point
  true
end
def animate_to_next_point
  @current_index += 1
  # keep current_index in the range [0,3]
  @current_index = @current_index % @points.count
  UIView.animateWithDuration(2,
    animations: lambda {
       @view.frame = [@points[@current_index], [100, 100]]
    },
    completion:lambda {|finished|
      self.animate_to_next_point
    }
  )
end
このコードには新しく登場したものが 2 つあります。最初に、[[0, 0], [100, 100]] という構文は frame のためのものです。入れ子にした配列はこれまで使ってきた CGRectMake を簡略化したものです。最初の配列は origin の値を表し、二つ目の配列は size の値を表しています。frame を変更するために @points を簡単に使用することができ、素晴らしいことです。
二つ目は lambda です。この用語に慣れていない人向けに説明すると、lambda は変数が割り当てられたり引数が渡されたりする関数です。たとえば、my_lambda = lambda { p "Hello!" } とすることができます。ここで my_lambda.call を実行すると、lambda ブロック内のコードが実行されます。lambda は引数を持つこともできます。
my_lambda = lambda { |name| p "Hello #{name}!" }
my_lambda.call("Clay")
=> Hello Clay
さて、アニメーションの例に戻りましょう。いつものような window を作成し、ボックスを移動するためにいくつかポイントを定義します。window にボックス (@view) を追加し、animate_to_next_point を開始することでよく知られたマジックが起こります。
UIView.animateWithDuration:animations:completion: はアニメーションを制御するものです。たくさんのパーツがこのメソッドにあるので、ひとつずつ見ていきましょう。
duration にはアニメーションをどのくらい継続するかを秒単位で設定します。1.85 のように float の値を使うこともできます。大まかには "素早い" アニメーションは .3 秒にするとよいです。
任意の ビューに対して animations: の lambda で行った変更がスムーズにアニメーションされます。なんてクールなんでしょう!?Android と比較すると、Android では望むアニメーションのために各プロパティの独自の Animation オブジェクトを作成する必要があります。さて、いくつかのプロパティはアニメーションできませんが、frame, bounds や alpha といった一般的なものは問題なく動きます (完全なリストを見たければ、 こちら をチェックしてください)。私たちの場合では、@view.frame の値を @points で用意してある次の origin に設定します。
最後に、completions: はアニメーションが終了した後で呼び出されます。この lambda では 必ず 引数を受け付ける必要があります。アニメーションが本当に終了したかを通知する boolean の値になります。他のアニメーションを明示的に行う場合にのみ、それによってアニメーションが取り消されるため、アニメーションは失敗となることがあります。
rake を実行し、シミュレータの左上の角をスライドする青いボックスが表示されます。とても簡単でしたよね?

私はこんなに簡単であったという衝撃を忘れることができません。さきほどのアニメーションに 1) delay を持たせる 2) 別の "アニメーションカーブ" を持たせる、と変更することでスパイスを振りかけましょう。慣れていない?現在のアニメーションを見てみましょう。各ポイントの間で、ボックスがどのくらいスピードアップしスローダウンしているかわかりますか?もっと近くで見てください。必ず分かるはずです。
この動きになる理由は、iOS のデフォルトのアニメーションカーブが UIViewAnimationOptionCurveEaseInOut だからです。実際の生活のなかで頻繁におこる動き方をシミュレートしていて本当に素晴らしいエフェクトです。私たちは素早く方向転換しませんし、徐々にスピードをあげます。いろいろ遊ぶために、別のカーブを使ってみましょう。
これまで使ってきた animateWithDuration:animations:completion: をより設定項目のあるものに変更します。animateWithDuration:delay:options:animations:completion: はアニメーションを開始する前に delay を追加し、また options を設定できます。delay は秒単位の値を取り、animations: ブロックの開始までのオフセットとなります。
options は少し複雑です。UIViewAnimationOption というプレフィックスの値で integer のビットマスクを取ります。(0 | 1 | 4) == 5 のように Ruby でビットマスクを行うことができます。リニアなアニメーションカーブと自動的にアニメーションを繰り返すというような、同時に複数のアニメーションオプションを組み合わせたい場合に備えビットマスクになっています(options: (UIViewAnimationOptionCurveLinear | UIViewAnimationOptionRepeat))。
(UIView.animate... でより設定項目が少ない animateWithDuration:animations: というものもあります)。
とにかく、動かしてみましょう。animate_to_next_point メソッドを次のように変更します。
def animate_to_next_point
  @current_index += 1
  @current_index = @current_index % @points.count
  UIView.animateWithDuration(2,
    delay: 1,
    options: UIViewAnimationOptionCurveLinear,
    animations: lambda {
       @view.frame = [@points[@current_index], [100, 100]]
    }, completion:lambda {|finished|
      self.animate_to_next_point
    })
end
rake を実行し、新しいアニメーションをチェックしましょう。
古くさいもののように見えますが、その動作はかなり異なっていますよね?ポイント間でいくらか時間がかかるようになり、スライドはよりメカニカルです。
You Know This Section
iOS の基本的なアニメーションを扱ってきました。これまでどんなことをやってきましたか?
animateWithDuration:animations:と類似したものを使い、ビューのプロパティをanimations:の lambda で変更することでアニメーションが動きました。- アニメーションカーブによってアニメーションの動作が決まります。
UIViewAnimationOptionCurveLinearとUIViewAnimationOptionCurveEaseInOutを例として扱いました。