2015年4月12日日曜日

Core Animation Lesson.(Lesson1-4〜)

前回(Core Animation Lesson1(Lesson1-1〜)) から引き続きCALayerのプロパティについて調査しました。

contentsScaleプロパティ(CALayer)

本プロパティ値はレイヤ上の背景画像とView上のピクセルサイズ比率を定義するプロパティです。要するにRetinaディスプレイか否かを設定します。contentsScaleの値は通常のディスプレイで1.0を指し、Retinaディスプレイの場合は2.0となります。デフォルト値は1.0です。 この値は以下の様にUIScreenクラスから取得するのが一般的のようです。

    // set contentsScale to The Retina value
    myView.layer.contentsScale = UIScreen.mainScreen().scale

但し、CALayerのcontentsGravityにfill/fit系の拡大縮小のオプションを指定している場合、contentsScaleの値意味を持たなくなるので、注意が必要です。

表. CALayerのcontentsScaleの値について
contentsScale 概要
1.0 1Pointが1Pixcelの通常ディスプレイを指す。
2.0 1Pointが2x2(4Pixcels)のRetinaディスプレイを指す。


下の表示イメージは同一画像をRetina/非Retinaで表示してみた例です。
図. Retinaディスプレイ[←左図](contentsScale=2.0)と
非Retinaディスプレイ[右図→](contentsScale=1.0)
masksToBoundsプロパティ(CALayer)

Viewよりも大きい画像はViewからはみ出して表示されてしまいますが、CALayerのmasksToBoundsプロパティを設定することで抑止できます。値はBool値でtrueが設定されるとマスクされます。また、このプロパティはUIViewのclipesToBindsプロパティと同等の機能となります。

図. masksToBounds=false

図. masksToBounds=true


以下、サンプルコードです。SegmentedControlボタンでcontentsScale, SwitchボタンでmasksToBoundsのオンオフを設定しています。

import UIKit

class Lesson1_4ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!
    @IBOutlet weak var mySegmentedControl: UISegmentedControl!
    @IBOutlet weak var mySwitch: UISwitch!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        
        // image
        let image = UIImage(named: "chess.png")
        myView.layer.contents = image?.CGImage
        myView.layer.contentsGravity = kCAGravityCenter
        
        // set default contentsScale to The Retina value
        myView.layer.contentsScale = UIScreen.mainScreen().scale
        // set default clip setting
        myView.layer.masksToBounds = true
        
        // set action for segmented control
        mySegmentedControl.addTarget(self, action: "segmentedValueChanged:", forControlEvents: UIControlEvents.ValueChanged)
        // set action for switch
        mySwitch.addTarget(self, action: "switchValueChanged:", forControlEvents: UIControlEvents.ValueChanged)
        
        // dump contentsScale
        println("contentsScale : \(myView.layer.contentsScale)")
        println("image Scale   : \(image?.scale)")
        println("UIScreen scale: \(UIScreen.mainScreen().scale)")
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    func segmentedValueChanged(target: UISegmentedControl) {
        if (target.selectedSegmentIndex == 0) {
            myView.layer.contentsScale = 1.0
            
        } else {
            myView.layer.contentsScale = 2.0
            
        }
    }
    
    func switchValueChanged(target: UISwitch) {
        if target.on {
            myView.layer.masksToBounds = true
        } else {
            myView.layer.masksToBounds = false
        }
    }
}
contentsRectプロパティ(CALayer)

指定された範囲をクリップするイメージですが、大きな画像ファイルに小さいイメージをパックする事で、小さいイメージを個々に読み込むより、大きなファイルを一回だけ読み込ませる方が読み込み時間、使用メモリ量のパフォーマンスに優れています。ゲームの様に小さな画像を多数使用する場合にメリットが大きい様です。また、contentsRectの単位はpoint単位では無くunit coordinate単位であることにも注意が必要です。

以下の図は、1枚の画像ファイルにAとBが横並びで描画されています。その画像ファイルを1回だけ読み込み、contentsRectプロパティを利用し元画像の必要な部分(AとB)をクリッピングし縦並びで表示しています。


以下、ソースコードですが、CALayerのcontentsには元画像を設定しcontentsRectプロパティにてそれぞれ必要な領域を指定しています。contentsRectはCGRect型で指定する値はUnitCoordinate単位となります。UnitCoordinate単位については後述します。

import UIKit

class Lesson1_5ViewController: UIViewController {

    @IBOutlet weak var viewA: UIView!
    @IBOutlet weak var viewB: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        // load image once.
        let image = UIImage(named: "AB.png")
        
        // A
        viewA.layer.contents = image?.CGImage
        viewA.layer.contentsRect = CGRectMake(0, 0, 0.5, 1.0)
        
        // B
        viewB.layer.contents = image?.CGImage
        viewB.layer.contentsRect = CGRectMake(0.5, 0, 0.5, 1.0)
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
Point, Unit単位について
表. Point, Unit単位について
Coordinate Type 概要
Point Pointは仮想的なPixelであり、デバイスの解像度によって変化する。通常デバイスでは1Pointが1Pixelとなるが、Retina Display では、2x2の計4Pixelsとなる。
Unit Unit単位は0.0〜1.0まで指定可能な比率で指定する便利な指定方法で、1.0が100%のサイズを意味している。比率で指定しているため、元の画像のサイズが変更されても再調整する必要が無いのが利点。

以下図はUnit単位を表しています。右下が最大値である(1, 1)を表し、中心点は(0,5, 0,5)となります。

図. Unit単位について
contentsCenterプロパティ(CALayer)

このプロパティは、プロパティ名から想像される機能と異なり、イメージの伸縮エリアについての設定が行われます。これは、インターフェースビルダーからも設定可能な値で、デフォルト値はUnit単位で(0,0,1,1)となり全域が一様に伸縮する設定となっています。contentCenterで指定した矩形の外側はそれぞれの位置により伸縮の方向が異なります。

以下のイメージを考えてみます。分かりやすいように方眼紙のようなマス目をが描かれているイメージを描画しています。この画像イメージにcontentsCenterプロパティを指定してみます。

図(↓)contentsCenterとして(0.25, 0.25, 0.5, 0.5)を指定しました。方眼紙のイメージで分かりやすいかと思ったのですが、全然分かり難いですね。良く見てみると分かりますが、方眼紙のマス目の高さや幅ががcontentsCenterで指定した矩形内と外で異なっていることに気づくと思います。

図(↓)contentsCenterで指定した矩形枠内と外での引き伸ばしについて、赤枠は縦横均等、緑は縦、青は横、黄色は無しとなっています。

contentsCenterはViewのプロパティとしてGUIからも設定可能です。


今回使用したサンプルプログラムはGithub上のCoreAnimationLessonに置いています。


0 件のコメント :

コメントを投稿