今回はCocoaProgrammingでSwiftを使った「正円」や「半円」それ以外の中途半端な弓形をビュー上に描画する方法を紹介します。
「addEllipse」だと正円や楕円しか描画できなかったのですが、「addArc」を使うことで270°の弓形なども描画できるようになるので参考にしてみてください。
もくじ
はじめに
今回紹介する方法は「CocoaProgramming」で動作を確認しており、使用している言語はSwiftです。
また、Xcodeを使用しているため、その他の言語やツールでは正しく機能しないので注意してください。
ではまず実際の動作を確認しましょう。
このように、正円だけではなく「270°」の弓形を描画したり、開始位置と終了位置を繋いだメーターのような図形を描画できるようになります。
弓形というのは、270°のような正円でも半円でもない、弓のような形をしている円のことを言います。
ではまず、アプリケーションに表示するボタンやカスタムビューを設置していきましょう。今回は「xib」で作成してみました。
xibを作成
今回作成したアプリケーションは、以下のようなオブジェクトで構成されています。
左から順に「正円」「235°」の弓形、「270°」の弓形となっています。
また、右側の270°の弓形は「円弧の開始点」と「終了点」を中心点と繋いだ「おうぎ形」を描画するようにしてみました。
これらのカスタムビューは「AppDelegate」とアウトレット接続されており、ボタンはまとめてアクション接続させてあります。
ではxibファイルが完成したので、続いて実際に図形を描画するコードを記述していきましょう。
プログラムの流れ
今回作成するプログラムは以下のような手順で行っていきます。
- 円弧を描画するクラスを追加
- ボタンを押した時の処理を記述
- ボタンのタイトルを判定して処理を分ける
- 作成したクラスを元にカスタムビューにビューを追加
- ボタンごとに描画される図形が違うことを確認
ボタンが押された時の処理
ボタンが押された時、どのボタンが押されたかによってそれぞれ処理を切り替えます。
今回の場合だと、以下のように記述することでそれぞれのカスタムビュー2秒ができるので確認してください。
@IBAction func pushButton(_ sender: NSButton) {
switch sender.title {
case "360°":
let views = PerfectCircle(frame: NSRect(x: 10, y: 10, width: circleCustomView.bounds.width - 10, height: circleCustomView.bounds.height - 10))
circleCustomView.addSubview(views)
break
case "235°":
let views = arcCircle(frame: NSRect(x: 10, y: 10, width: circleCustomView2.bounds.width-10, height: circleCustomView2.bounds.height-10))
circleCustomView2.addSubview(views)
break
case "270°":
let views = fanCircle(frame: NSRect(x: 10, y: 10, width: circleCustomView3.bounds.width-10, height: circleCustomView3.bounds.height-10))
circleCustomView3.addSubview(views)
break
default:
break
}
}
このようなコードを追加しました。
※この段階ではクラスが存在しないためエラーがでます。
ではまず、正円を描画するクラスを作成しましょう。
正円を描画
先日紹介した「addEllipse」を使えば、addArcよりも簡単に図形を描画できるのですが、こちらは正円や楕円を描画するためのコードでした。
今回はより細かい違いを確認するため、正円の描画にもaddArcを使用しますが、正円を描画したい時は可読性も上がるためaddEllipseを使うと良いかと思います。
ではaddArcを使った正円を描画するクラスを確認してください。
class PerfectCircle: NSView{
override func draw(_ dirtyRect: NSRect) {
let context = NSGraphicsContext.current()?.cgContext
let circleCenter = NSPoint(x: dirtyRect.width/2, y: dirtyRect.height/2)
let circleRadius = dirtyRect.width/2 - 10
let circleStartAngle = CGFloat(degreeToRadian(angle: 0))
let circleEndAngle = CGFloat(degreeToRadian(angle: 360))
let circleClockwise = true
NSColor.blue.set()
context?.setLineWidth(5.0)
context?.addArc(center: circleCenter, radius: circleRadius, startAngle:circleStartAngle, endAngle: circleEndAngle, clockwise: circleClockwise)
context?.strokePath()
}
func degreeToRadian(angle: Int) -> Double {
let radian = Double(-angle - 90) * M_PI/180
return radian
}
}
順番に解説していきます。
context
まず、contextですが、こちらはNSGraphicsContextを使うための変数で、このcontextに描画する座標や色などを設定していきます。
circleCenter
circleCenterでは、描画する円の中心座標を設定しており、dirtyRectで受け取った範囲の中心を設定してあります。
circleRadius
読んで字のごとくですが、円の半径を設定しています。
今回の場合は、ビューの横幅の半分から10を引いて、余白をつけてみました。
Angle
circleStartAngleとcircleEndAngleはそれぞれ、円弧の開始点と終了点です。
これらは対比の関係にあるため一緒に解説しますが、どちらもCGFloatで設定してください。
今回は「degreeToRadian」というメソッドを作成し、円の中心角度をラジアンに変換させました。
ラジアンの計算方法は「radian = 角度 * π / 180」です。
- 90 としているのは、通常時だと、開始点が時計でいう「3時」の点になってしまうためで、90をプラスしておくことで「12時」を開始点にできます。
開始円を0、終了点を350°に設定して、90をプラスした場合とそうでない場合を比較してみてください。
+90した場合:
しなかった場合:
正円の場合はどちらも関係ないのですが、せっかくaddArcを使うので覚えておきましょう。
また、後述する正円以外を描画する時には詳しく解説せず、ここでまとめてしまうので注意してください。
circleClockwise
circleClockwiseでは、円が「時計回りかどうか」を設定しています。
ここでいう時計回りというのは、開始点から終了点まで描画する時にどちらから進むかです。
先ほど、開始点0で終了点350に設定した時はほぼ正円が完成しましたが、こちらのclockwiseをfalseに変更した時はどのようになるのか確認してください。
このように先ほど描画された範囲と逆になりましたね。
ここからも分かる通り、trueで時計回り、falseで反時計回りで設定できます。
NSColor.blue.set()
こちらは、addEllipseの記事でも紹介したのですが「パスに色を設定」するためのコードです。
今回の場合は「青い円弧」を描画したかったため「NSColor.blue.set( )」になりました。同様に、黒や赤などの色もセットすることができます。
context?.setLineWidth
こちらはそのままの意味で、線の太さを設定できます。
値はFloat型で設定でき、値を増やすことで線も太くなっていきます。
今回の場合はstrokeで描画したのでsetLineWidthを使用しましたが、塗りつぶしの図形を描画する際はこの記述は不要です。
context?.addArc
いよいよメインの「addArc」です。
こちらは、幾つかの引数を取ることができ、以下のように設定します。
ここまで紹介してきたそれぞれの変数を割り当てることで、円弧が描画できるようになりました。
では正円が描画できたか確認してみましょう。
このようになりましたか。
ここまでくれば、あとは角度を変えて円を描画するだけです。
一気に見ていきましょう。
235°の円を描画
ここまででコードの解説は大方終了しているので、先に235°の弓形を描画するためのコードを確認してください。
class arcCircle: NSView{
override func draw(_ dirtyRect: NSRect) {
let context = NSGraphicsContext.current()?.cgContext
let circleCenter = NSPoint(x: dirtyRect.width/2, y: dirtyRect.height/2)
let circleRadius = dirtyRect.width/2 - 10
let circleStartAngle = CGFloat(degreeToRadian(angle: 0))
let circleEndAngle = CGFloat(degreeToRadian(angle: 235))
let circleClockwise = true
NSColor.blue.set()
context?.setLineWidth(5.0)
context?.addArc(center: circleCenter, radius: circleRadius, startAngle:circleStartAngle, endAngle: circleEndAngle, clockwise: circleClockwise)
context?.closePath()
context?.strokePath()
}
func degreeToRadian(angle: Int) -> Double {
let radian = Double(-angle+90) * M_PI/180
return radian
}
}
235°の円弧を描画するにはこのような記述をします。
先ほどは登場していない「closePath( )」という記述が増えている点に着目してください。
このような記述を追加することで「開始点」と「終了点」を繋ぐ直線を描画できるようになります。
ですので、開始点から終了点までの直線を描画したい時はclosePathを使うことで、わざわざ座標を取得する必要はなく、簡単にパスを閉じることができます。
最後におうぎ形(扇形)を描画する方法です。
おうぎ形を描画する
おうぎ形を描画する方法は、先ほどの235°の円を描画した時と似ているのですが、ここに一つ手順を加えます。
2通りの方法で実装できるのですが、どちらも中心点をポイントに設定することで描画できます。
以下を確認してください。
class fanCircle: NSView{
override func draw(_ dirtyRect: NSRect) {
let context = NSGraphicsContext.current()?.cgContext
let circleCenter = NSPoint(x: dirtyRect.width/2, y: dirtyRect.height/2)
let circleRadius = dirtyRect.width/2 - 10
let circleStartAngle = CGFloat(degreeToRadian(angle: 0))
let circleEndAngle = CGFloat(degreeToRadian(angle: 270))
let circleClockwise = true
NSColor.blue.set()
context?.setLineWidth(5.0)
context?.addArc(center: circleCenter, radius: circleRadius, startAngle:circleStartAngle, endAngle: circleEndAngle, clockwise: circleClockwise)
context?.closePath()
context?.strokePath()
Swift.print(circleEndAngle)
}
func degreeToRadian(angle: Int) -> Double {
let radian = Double(-angle+90) * M_PI/180
return radian
}
}
こちらは先ほどの235°のコードをそのまま270°に書き換えただけなので、以下のようになります。
このような図形をおうぎ形にする場合、2通りの方法があると紹介しました。
まずは「中心点と円弧をつなぐ」方法から紹介します。
中心点と円弧をつなぐ
先ほどのコードでaddArcの前に以下のような記述を追加してください。
context?.move(to: circleCenter)
このコードは描画開始の座標を設定するもので、円の中心からaddArcで指定した弧を通る図形を描画するという意味合いになります。
closePathが記述されているため、円弧の終了点から中心点を繋ぐ直線が描画されます。
ちなみにclosePathを記述しないと以下のようになります。
2つ目の方法は、円弧に中心点へつなぐ直線を追加する方法です。
円弧と中心点をつなぐ
addArcの後ろに以下の記述を追加してください。
context?.addLine(to: circleCenter)
先ほどの場合とは違い、addLineは「直線を追加する」コードになっています。
moveと似ていますが全然違うコードなので覚えておきましょう。
実行結果は先ほどと同じなので割愛します。
まとめ
今回紹介した「addArc」を使うことで、ビュー上におうぎ形や弓形のような図形を描画できるようになりました。
moveやclosePath、addLineはaddArc以外の図形を描画する時にも登場するコードなので、ぜひ覚えておきましょう。
ではまた。