Swift CAShapeLayerで図形にグラデーションをつける方法
UIViewにCAShapeLayerを投入し図形を表示できる。CAShapeLayerのプロパティには、fillColor(背景を塗る色指定)やstrokeColor(図形の輪郭を書く時の色指定)が用意されているので、簡単に図形に色を塗ることができる。
ではCAShapeLayerで背景色にグラデーションをつける場合はどうするのか?
毎回忘れるのと誤解していたので整理がてらメモ。
CALayerのmask
グラデーションを付ける前にCALayerのmaskの機能を見てみる。
mask — CALayer | Apple Developer Documentation
var mask: CALayer? { get set }
これは、別のCALayerを mask にセットすると、もともとのCAlayerの図形が「マスク」され部分的に表示されるという機能である。
maskサンプル
これはmaskを利用した例。赤い星のCAShapeLayerに 黒丸4つのCAShapeLayerをmaskとして設定すると、星の一部が切り抜かれて表示される。
黒丸4つのマスクと重なった部分が表示されたのだ。
以下Swiftコード。
- 黒丸マスク CAShapeLayer
// mask path
let maskpath = CGMutablePath()
maskpath.addEllipse(in: CGRect(x: 10, y: 10, width:30, height:30))
maskpath.addEllipse(in: CGRect(x: 10, y: 60, width:30, height:30))
maskpath.addEllipse(in: CGRect(x: 60, y: 10, width:30, height:30))
maskpath.addEllipse(in: CGRect(x: 60, y: 60, width:30, height:30))// show path on layer
let maskLayer = CAShapeLayer()
maskLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
maskLayer.path = maskpath
maskLayer.fillColor = UIColor.black.cgColor
- 星マスク CAShapeLayer
// create path of ☆
let starPath = CGMutablePath()
starPath.move(to: CGPoint(x:19.098,y:100))
starPath.addLine(to: CGPoint(x:25,y:63.82))
starPath.addLine(to: CGPoint(x:-0,y:38.197))
starPath.addLine(to: CGPoint(x:34.549,y:32.918))
starPath.addLine(to: CGPoint(x:50,y:-0))
starPath.addLine(to: CGPoint(x:65.451,y:32.918))
starPath.addLine(to: CGPoint(x:100,y:38.197))
starPath.addLine(to: CGPoint(x:75,y:63.82))
starPath.addLine(to: CGPoint(x:80.902,y:100))
starPath.addLine(to: CGPoint(x:50,y:82.918))
starPath.closeSubpath()// show path on layer
let shapeLayer = CAShapeLayer()
shapeLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
shapeLayer.path = starPathshapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 2
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.borderColor = UIColor.gray.cgColor
- 星マスクに黒丸マスクをつけて UIViewに表示
shapeLayer.mask = maskLayer
view.layer.addSublayer(shapeLayer)
アニメーションするmask
今度はさっきの赤い星に、アニメーションする円をmaskとして指定してみよう。
期待通り、星型と円が重なるものがアニメーション表示された。
- アニメーションする円のSwiftコード
// mask path
let maskpath = CGMutablePath()
maskpath.addEllipse(in: CGRect(x: 50, y: 50, width: 0, height: 0))let maskpath2 = CGMutablePath()
maskpath2.addEllipse(in: CGRect(x: 0, y: 0, width: 100, height: 100))let maskLayer = CAShapeLayer()
maskLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
maskLayer.path = maskpathmaskLayer.fillColor = UIColor.black.cgColor// アニメーション指定
let pathKeyframe = CAKeyframeAnimation(keyPath: "path")
pathKeyframe.values = [maskpath,maskpath2,maskpath]
pathKeyframe.keyTimes = [0, 0.5, 1]
pathKeyframe.duration = 3.0
pathKeyframe.repeatDuration = CFTimeInterval.infinity
pathKeyframe.isRemovedOnCompletion = false
maskLayer.add(pathKeyframe, forKey: "path")
それで、図形にグラデーションをつける方法は?
勘の良い人ならこれでおわかりかもしれない。 グラデーションを描くCAGradientLayerというCALayerがある。そのCAGradientLayerのmaskに希望する形のCALayerを指定すればよいのだ。
CAGradientLayerでグラデーション描くとこうなった。
- CAGradientLayer グラデーションのSwiftコード
let gradLayer = CAGradientLayer()
gradLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
gradLayer.colors = [
UIColor.red.cgColor,
UIColor.blue.cgColor,
]
view.layer.addSublayer(gradLayer)
じゃあ、このグラデーションをマスクしちゃえばよさそう。
gradLayer.mask = maskLayer
同様に星型のレイヤをgradLayer.mask
に指定すれば、星型に抜くことができるし、アニメーションもmaskに指定することができる!
何を勘違いしていたか
作画ソフトを使うと図形にグラデーションをつけるというイメージなので、CAShapeLayerにグラデーション指定用のプロパティがあるかと思っていました…グラデーションをマスクするのですね。どうもこのあたりに違和感がありましたが、きちんと調べたら理解できました。
もちろんCALayerやUIViewを自前で描画する方法もあると思いますが、一例として。