LoginSignup
8

More than 1 year has passed since last update.

CAShapeLayer による図形のストロークアニメーション

Last updated at Posted at 2019-12-15

iOS で図形を描画したい場合、CAShapeLayer を使うと、様々な図形を描画することができます。
また、Core Animation を使うことで、図形をアニメーションさせることができます。

簡単な実例を Xcode の Playground で試してみます。

Playground の準備

まず、Xcode で iOS 用の Playground を作成して、View を表示します。

import UIKit
import PlaygroundSupport

let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 400, height: 400)
view.backgroundColor = .white
PlaygroundPage.current.liveView = view

View のサイズはなんでも良いですが、とりあえず 400x400 にしておきました。次のように白い View が表示されます。

Playground.png

CAShapeLayer の作成

先ほどの View に CAShapeLayer を追加します。

let shapeLayer = CAShapeLayer()
shapeLayer.frame = CGRect(x: 0, y: 0, width: 400, height: 400)
shapeLayer.lineWidth = 20.0
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.cyan.cgColor
view.layer.addSublayer(shapeLayer)

まだ図形を設定していませんが、ひとまず線の幅(lineWidth)、線の色(strokeColor)、塗りつぶしの色(fillColor)を設定しておきます。

ここでは、単に何も表示しない shapeLayer を追加しただけなので、表示は変わりません。

弧の描画

弧を描画してみます。

let center = CGPoint(x: 200, y: 200)
let radius = CGFloat(100)
let startAngle = CGFloat(0.125 * .pi)
let endAngle = CGFloat(1.875 * .pi)

let arc = UIBezierPath(arcCenter: center,
                       radius: radius,
                       startAngle: startAngle,
                       endAngle: endAngle,
                       clockwise: true)
shapeLayer.path = arc.cgPath

UIBezierPath で、中心座標、半径、開始角、終了角を指定すれば、弧が作成できます。

角度の数値については リファレンス に記載されています。3時の方向を0として、時計周りにラジアンで指定します(数学では反時計回りが一般的ですが、ここでは逆向きです)。ここでは、1/8 π ラジアンから、(2 - 1/8) π ラジアンまでの円弧を指定しました。

Arc.png

弧のストロークアニメーション

弧の描画をアニメーションで表示します。

let animation = CAKeyframeAnimation(keyPath: "strokeEnd")
animation.values = [0.0, 1.0, 1.0]
animation.keyTimes = [0.0, 0.3, 1.0]
animation.duration = 3.0
animation.repeatCount = .greatestFiniteMagnitude
shapeLayer.add(animation, forKey: nil)

CAShapeLayer は様々なアニメーションが可能ですが、今回は strokeEnd を使います。

strokeStartstrokeEnd はペアで使うプロパティで、CAShapeLayerpath のうち、実際に描画する範囲を 0.0 から 1.0 の間で指定します。デフォルトでは strokeStart0.0strokeEnd1.0、つまり path の全体を描画します。仮に strokeEnd0.5 にすると、path の最初から半分の位置までが描画されます。

strokeEndリファレンス にも書いてあるように Animatable なプロパティであり、値の変化をアニメーションで表示できます。そこで、strokeEnd0.0 から 1.0 にアニメーションで変化させることで、線が徐々に描画されるアニメーションになります。

また、アニメーションには CABasicAnimation がよく使われますが、今回は CAKeyframeAnimation を使いました。

valueskeyTimes を使って値の変化を指定します。上記のコードは、時間 0.0 から 0.3 の間に値を 0.0 から 1.0 に変化させ、時間 0.3 から 1.0 の間では値は 1.0 から 1.0 のままにする、という指定をしています。アニメーション全体の時間 duration は 3 秒に指定しているため、時間 0.3 は 3 x 0.3 = 0.9 秒を意味します。

アニメーションを繰り返す場合に、いったん静止状態を挟むのは CAKeyframeAnimation が簡単かと思います。特に繰り返しが必要なければ、CABasicAnimation を使うほうが素直で分かりやすいかと思います。

扇形の描画

複数の線を連結させて図形を描画してみます。

let center = CGPoint(x: 200, y: 200)
let radius = CGFloat(100)
let startAngle = CGFloat(0.125 * .pi)
let endAngle = CGFloat(1.875 * .pi)

let fanShape = UIBezierPath()
fanShape.move(to: center)
fanShape.addLine(to: CGPoint(x: center.x + radius * cos(startAngle),
                             y: center.y + radius * sin(startAngle)))
fanShape.addArc(withCenter: center,
                radius: radius,
                startAngle: startAngle,
                endAngle: endAngle,
                clockwise: true)
fanShape.close()
shapeLayer.path = fanShape.cgPath

まず、円の中心にペンを置き(move)、円周に向かって線を引きます(addLine)。ある点から距離 radius、角度 θ 方向の点の座標は、X 座標に radius * cos(θ) を、Y 座標に radius * sin(θ) を足せば求められます。

次に、この線に続けて弧を書きます(addArc)。最後に close を呼ぶと、最初の点まで直線で繋がれて閉じた線ができあがります(なお、close しないと閉じた線という扱いにならないため、始点と終点が繋がって描画されずに不自然になります)。

FanShape.png

扇形のストロークアニメーション

扇形の描画をアニメーションで表示するコードは、弧のときとまったく同じです。

let animation = CAKeyframeAnimation(keyPath: "strokeEnd")
animation.values = [0.0, 1.0, 1.0]
animation.keyTimes = [0.0, 0.3, 1.0]
animation.duration = 3.0
animation.repeatCount = .greatestFiniteMagnitude
shapeLayer.add(animation, forKey: nil)

どんな図形でも strokeEnd0.0 から 1.0 に変化させれば良いので、難しいことを考える必要はありません。

まとめ

Xcode Playground で今すぐ試せるアニメーションのコードを紹介しました。参考になれば幸いです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8