2015年3月16日月曜日

Core Animation Lesson.(Lesson1)

Core Animationに関して、画像、座標、エフェクト、トランスフォーム、アニメーション等をシリーズ化してまとめていきたいと考えています。

Core Animationとは

Core Animationと聞くと、なにかアニメーションに特化した小難しいFrameworkのような印象を受けてしまいますが、アニメーション自体はCore Animationが提供する数ある特徴の一部であり、アニメーションだけに特化したFrameworkではありません。Core Animationはビジュアルコンテンツを画面描画する機能群から構成された共通基盤で、より高速に動作するよう設計されています。

UIViewとCALayer

Core Animation はどの様にして使うのでしょうか。それは、UIViewクラスで既に使用されています。 UIViewは、画像やテキスト、動画等のコンテンツの描画、タッチやジェスチャー入力に対応するクラスで、subviewとして子のViewを階層構造で管理しています。加えて、Core Graphicsベースの描画、アファイン変換(回転や拡大縮小)と簡単なアニメーションをサポートします。それら機能はCore AnimationのCALayerというクラスにより提供されています。

CALayerはUIViewととても良く似たコンセプトで設計されています。UIViewは矩形のオブジェクトでViewを階層構造で管理可能です。同様にCALayerも矩形オブジェクトであり、Layer自身に子のLayerをsublayersとして追加することが可能で親子関係を構築する事が出来ます。

Viewの階層構造イメージ図(View Debuggingしただけですが)

CALayerがUIViewと大きく異なっている点は、CALayer自身はレスポンダチェインに属さないために、タッチイベントを処理出来ないことに加えて、主に以下の特徴が挙げられます。

CALayerの主な特徴
  • ドロップシャドウ、角丸、カラードボーダー
  • 3D座量変換、ポジショニング
  • アルファマスク
  • マルチステップ、非連続的なアニメーション
※これら機能は改めて紹介したいと思います。
背景描画について(Lesson1)

CALayerを使用した背景色・背景画像の表示と、背景画像の見せ方(表示位置・拡大/縮小)に関するUIView及びCALayerのプロパティ値について。

単色背景の描画(Lesson1-1)

それでは、CALayerを使用して背景を描画してみましょう。UIViewを生成すると、iOSの場合デフォルトで必ず1つCALayerが生成されます。そのCALayerは使用せずに、新たに一つCALayerを生成して描画してみます。

青色の固定サイズの矩形を明示的に新しいCALayerを作成して、青色の背景色を設定します。CALayerのaddSublayerメソッドで新しいLayerを追加します。 サンプル実装ではUIVIew を1つ追加し、その上に描画します。

import UIKit

class Lesson1_1ViewController: UIViewController {
    @IBOutlet weak var myView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // create new layer
        let myLayer = CALayer()
        myLayer.frame = CGRectMake(10, 10, 200, 200)
        myLayer.backgroundColor = UIColor.blueColor().CGColor

        // add new layer to backing layer
        myView.layer.addSublayer(myLayer)
        
    }
}

CALayerのbackgroundColorプロパティはUIViewクラスのそれがUIColor?型であるのと違い、CGColor!型であることに注意して下さい。

画像の描画(Lesson1-2)

それでは簡単な画像を描画してみます。画像はUIImageのインスタンスを作成し、そのCGImageプロパティ値をCALayerのcontentsプロパティへ設定しているだけです。Lesson1-1と違ってCALayerはUIViewで暗黙的に作成されたCALayerを使用しています。このレイヤはUIViewクラスのlayerプロパティで参照することが出来ます。因みに、画像はチェスの駒です。

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // initialize segmented control
        mySegmentedControl.addTarget(self, action: "segmentedValueChanged:", forControlEvents: UIControlEvents.ValueChanged)


        // image content
        let image = UIImage(named: "chess.png")
        
        // set image to layer contents
        myView.layer.contents = image?.CGImage
        
    }

残念なことに、よく見てみると駒が少し太って表示されていることに気付くと思います。 画像を枠内にどのように表示するかはUIViewクラスのcontentModeプロパティで設定することが出来ます。この値は、UIViewContentMode列挙型で定義されていて、デフォルトはScaleToFillとなっているため画面一杯に表示されることになります。正しい比率を維持したまま画面に表示するにはScaleAspectFitを設定すると良いでしょう。

myView.contentMode = UIViewContentMode.ScaleAspectFit
枠内にフィットするように画像サイズを自動調整(アスペクト比は維持)

UIViewContentModeの各値は以下の様に定義されています。詳細はドキュメントを参照下さい。

enum UIViewContentMode : Int {
    case ScaleToFill    // default 
    case ScaleAspectFit // 枠内にフィットするように自動調整(同アスペクト比)
    case ScaleAspectFill// 枠内を埋めるように自動調整(同アスペクト比)
    case Redraw
    case Center
    case Top
    case Bottom
    case Left
    case Right
    case TopLeft
    case TopRight
    case BottomLeft
    case BottomRight
}
UIViewContentMode.ScaleAspectFill
UIViewContentMode.Center
UIViewContentMode.Bottom
CALayerによる画像サイズ調整(Lesson1-3)

Lesson1-2ではUIViewクラスのcontentModeプロパティにより画像表示を調整しましたが、CALayerクラスにも同様のプロパティ値が存在します。それが、contentsGravityプロパティです。 contentsGravityはNSString型で定義されています。 正しい比率を維持したまま画面に表示するにはkCAGravityResizeAspectを指定します。

let kCAGravityCenter: NSString!
let kCAGravityTop: NSString!              // NOTICE!
let kCAGravityBottom: NSString!           // NOTICE!
let kCAGravityLeft: NSString!
let kCAGravityRight: NSString!
let kCAGravityTopLeft: NSString!
let kCAGravityTopRight: NSString!
let kCAGravityBottomLeft: NSString!
let kCAGravityBottomRight: NSString!
let kCAGravityResize: NSString!           // default
let kCAGravityResizeAspect: NSString!     // 枠内にフィットするように自動生成(同アスペクト比)
let kCAGravityResizeAspectFill: NSString! // 枠内を埋めるように自動調整(同アスペクト比)

しかし、kCAGravityTop, kCAGravityBottom を指定すると表示が上下逆になることに気付きます。これはiOSとOS XによるデフォルトのY軸の向きが異なることが原因で、iOSの場合は左上、OS Xの場合は左下が起点となります。 geometryFlippedプロパティをtrueに設定すれば思った通りの結果になるはずです。

iOS/OS XのY軸向きの違い

続きは次回。次回はCALayerのプロパティであるcontentsScale/masksToBounds/contentsRect/contentsCenter について調査したいと考えています。

説明に使用したサンプル・アプリケーションはGitHub上のCoreAnimationLessonとして配置していますので、参考にどうぞ。今後CoreAnimationに関するサンプルは本リポジトリ上に実装していく予定です。


0 件のコメント :

コメントを投稿