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 が表示されます。
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) π ラジアンまでの円弧を指定しました。
弧のストロークアニメーション
弧の描画をアニメーションで表示します。
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
を使います。
strokeStart
と strokeEnd
はペアで使うプロパティで、CAShapeLayer
の path
のうち、実際に描画する範囲を 0.0
から 1.0
の間で指定します。デフォルトでは strokeStart
が 0.0
で strokeEnd
が 1.0
、つまり path
の全体を描画します。仮に strokeEnd
を 0.5
にすると、path
の最初から半分の位置までが描画されます。
strokeEnd
は リファレンス にも書いてあるように Animatable なプロパティであり、値の変化をアニメーションで表示できます。そこで、strokeEnd
を 0.0
から 1.0
にアニメーションで変化させることで、線が徐々に描画されるアニメーションになります。
また、アニメーションには CABasicAnimation
がよく使われますが、今回は CAKeyframeAnimation
を使いました。
values
と keyTimes
を使って値の変化を指定します。上記のコードは、時間 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
を使うほうが素直で分かりやすいかと思います。
弧のストロークアニメーション pic.twitter.com/Of29omEqaE
— 宇佐見 公輔 (@usamik26) December 15, 2019
扇形の描画
複数の線を連結させて図形を描画してみます。
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
しないと閉じた線という扱いにならないため、始点と終点が繋がって描画されずに不自然になります)。
扇形のストロークアニメーション
扇形の描画をアニメーションで表示するコードは、弧のときとまったく同じです。
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)
どんな図形でも strokeEnd
を 0.0
から 1.0
に変化させれば良いので、難しいことを考える必要はありません。
扇形のストロークアニメーション pic.twitter.com/z4HFOG3YNe
— 宇佐見 公輔 (@usamik26) December 15, 2019
まとめ
Xcode Playground で今すぐ試せるアニメーションのコードを紹介しました。参考になれば幸いです。