【Swift】CALayerクラスとは?UIVewとの違いとアニメーションの実装
この記事からわかること
- SwiftのCALayerクラスとは?
- Core Animationフレームワークの役割
- UIViewとの違い
- レイヤーの実装方法
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
CALayerクラスならびにCore Animationフレームワークについて気になったのでリファレンスを読んでまとめてみました。至らぬ点や誤った理解があれば教えていただけると嬉しいです。
Core Animationフレームワーク
公式リファレンス:Core Animation
公式リファレンス:Core Animation Programming Guide
Core Animationフレームワークとはグラフィックレンダリングとアニメーションを提供するiOSとMac OS共通のフレームワークです。直接使用する機会は少ないですがiOSアプリにとってもインフラ部分になる大事なクラスです。
描画に特化しているのでイベント処理などを持たせることはできませんがその分描画速度が速く、GPUが処理を負担するためCPUへの負担も少ないのが大きな特徴になっています。
描画をGPUが担当するためBitmap形式でのデータ保存が必要になります。Core AnimationではBitmap形式でコンテンツを取り扱い、それをキャッシュ(「バッキングストア:backing store」と呼ぶ)することで高速な描画を実現しています。
※Bitmapとは画像のデータ形式の1つでデータをピクセルの位置(座標)と色で管理する形式です。
まとめると「コンテンツを画像で管理することでGPUに処理を任せて高速に描画するためのフレームワーク」という感じでしょうか?
そしてCore Animationで肝になってくるのがCALayer(レイヤーオブジェクト)です。
CALayerクラスとは?
CALayer
クラスはコンテンツを表示するための機能を提供するオブジェクトです。レイヤーとは日本語で「層」を意味する英単語です。
このレイヤーオブジェクトがコンテンツやジオメトリ(形状)、視覚的属性情報などBitmap形式のために必要な情報を管理します。ビューと似たような特徴を持っているため混同しがちですがあくまで情報だけを管理しておりモデルとして位置付けされており、ビューとは異なり独自の外観は定義しません。
※ジオメトリとは描画対象を表す座標や長さなどの情報のこと。
使われている場所
iOS(UIKit)では実際にCALayer
オブジェクトはUIViewクラスと紐づいています。UIViewにはCALayer
型のlayer
プロパティが用意され、1つのビューオブジェクトに対してレイヤーオブジェクトが自動で生成されるようになっています。これを「 レイヤーバックビュー(layer-backed-view)」と呼びます。
var layer: CALayer { get }
またUIViewはCALayerをラップしているため描画を操作すると相互に影響しあいます。
view. backgroundColor = .red
print(view.layer.backgroundColor) // Optional(<CGColor 0x6000028e8000> [<CGColorSpace 0x6000028f8a20> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 1 0 0 1 ))
どちらも描画に関するクラスですが、両者の役割は異なります。
UIView
・・・描画やイベント処理などを担当
CALayer
・・・描画する内容(画像情報)を管理
役割は異なりますが階層構造で管理される点は同じです。CALayerもUIView同様にスーパーレイヤー(親)とサブレイヤー(子)の関係を持つ階層構造で管理されています。
おすすめ記事:【Swift/UIKit】UIViewクラスとは?使い方やプロパティまとめ
アンダーライングレイヤーとスタンドアロンレイヤー
階層構造で管理されているためスーパーレイヤー(親)の配下にサブレイヤー(子)を追加できることがわかりました。追加するためには新しくレイヤーオブジェクトを生成し、addSublayer
メソッドを使って追加します。
最初からviewに対応したlayerを「アンダーライングレイヤー(underlying layer)」と呼び、個別に新しく生成しまだビューと紐づいていないレイヤーを「スタンドアロンレイヤー(standalone layer)」と呼びます。
view.layer // アンダーライングレイヤー
let myLayer = CALayer() // スタンドアロンレイヤー
用語まとめ
- バッキングストア(backing store):Bitmap形式でキャッシュすること
- レイヤーバックビュー(layer-backed-view):レイヤーと対応しているビューのこと
- アンダーライングレイヤー(underlying layer):ビューと対応しているレイヤーのこと
- スタンドアロンレイヤー(standalone layer):ビューと対応していないレイヤーのこと
実際にレイヤーを追加してみる
実際にレイヤーを追加してみます。紐付けたUIView
を用意して新しいレイヤーオブジェクト(スタンドアロンレイヤー)を生成し、ルートレイヤーの配下に追加してみます。
class ViewController: UIViewController {
@IBOutlet weak var rootView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let newLayer = CALayer()
newLayer.backgroundColor = UIColor.orange.cgColor
newLayer.anchorPoint = view.center
newLayer.frame = CGRect(x: view.center.x - 40 , y: view.center.y - 40 , width: 80, height: 80)
newLayer.opacity = 0.8
newLayer.cornerRadius = 5
rootView.layer.addSublayer(newLayer)
}
}
今回は中心に少しだけ透過させて角を丸くした四角形を表示してみました。カラーを指定する際はUIColor
ではなくCGColor
になるので注意してください。ちなみにCGは「Core Graphics」の略称です。
おすすめ記事:【Swift】CGRectの使い方!Core Graphicsとは?
レイヤー階層の操作
レイヤー階層は追加/挿入/削除/交換などの操作が可能になっています。先ほどは追加(addSublayer)の例です。
用意されているメソッド
メソッド | 概要 |
---|---|
addSublayer | 追加 |
insertSublayer:above: insertSublayer:atIndex: insertSublayer:below: |
挿入 |
removeFromSuperlayer | 削除 |
replaceSublayer:with: | 交換 |
またレイヤーに対して回転や透明度の設定など何かしら変更をするとサブレイヤーにも影響を与えます。
アニメーションを実装してみる
追加したレイヤーに「ボタンクリックで透明度を変換させる」アニメーションを実装してみたいと思います。
class ViewController: UIViewController {
@IBOutlet weak var rootView: UIView!
var newLayer:CALayer = CALayer()
override func viewDidLoad() {
super.viewDidLoad()
newLayer.backgroundColor = UIColor.orange.cgColor
newLayer.anchorPoint = view.center
newLayer.frame = CGRect(x: view.center.x - 40 , y: view.center.y - 40 , width: 80, height: 80)
newLayer.cornerRadius = 5
rootView.layer.addSublayer(newLayer)
}
@IBAction func changeOpacity() {
if newLayer.opacity == 1.0 {
// この変化でアニメーションが実装される
newLayer.opacity = 0.1
} else {
newLayer.opacity = 1.0
}
}
}
追加したレイヤーの透明度(opacity)の値を変更するだけで滑らかにビューが変化するようになっているので試してみてください。
アニメーションが実行されない?
先ほどの感覚でアンダーライングレイヤーにもアニメーションを実装してみようとするとこちらはアニメーションが実行されずにパッと切り替わる変化になってしまいます。これはアンダーライングレイヤーの場合は内部的にアニメーションがオフになっているために起きてしまうようです。
class ViewController: UIViewController {
var label: UILabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
label.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
label.center = view.center
label.backgroundColor = .brown
label.layer.cornerRadius = 5
label.clipsToBounds = true
view.addSubview(label)
}
@IBAction func changeOpacity() {
if label.layer.opacity == 1.0 {
// この変化ではアニメーションが実装されない
label.layer.opacity = 0.1
} else {
label.layer.opacity = 1.0
}
}
}
Core Animationでのより複雑なアニメーション実装は以下の記事を参考にしてください。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。