Path で線を引く ― move・addLine・addArc の基礎
組み込みシェイプだけでは表現できない図形を描きたいとき、Path の出番になる。Path は座標を指定して自由に線を引けるプリミティブで、SwiftUI のカスタム描画の土台となる存在だ。
Path の基本構造
Path はクロージャの中で描画命令を積み重ねて図形を構築する。最も基本的な操作は move(to:) で描画開始点を設定し、addLine(to:) で直線を引くことだ。
Path { path in
path.move(to: CGPoint(x: 10, y: 10))
path.addLine(to: CGPoint(x: 200, y: 10))
path.addLine(to: CGPoint(x: 200, y: 150))
}
.stroke(.blue, lineWidth: 2)move(to:) はペンを持ち上げて指定座標に移動する操作、addLine(to:) はペンを紙に押し当てたまま線を引く操作にあたる。この 2 つだけで、直線で構成されるあらゆる図形を描ける。
三角形を描く
Path で最初に試す定番が三角形だ。3 つの頂点を addLine で結び、closeSubpath で始点に戻す。
Path { path in
path.move(to: CGPoint(x: 100, y: 10))
path.addLine(to: CGPoint(x: 190, y: 170))
path.addLine(to: CGPoint(x: 10, y: 170))
path.closeSubpath()
}
.fill(.orange)closeSubpath() は現在の位置から始点まで自動的に直線を引いてパスを閉じるメソッドである。手動で始点へ addLine しても見た目は同じだが、closeSubpath を使うと線の結合処理(line join)が正しく適用され、角の描画が綺麗になる。
パスを正式に閉じる。stroke 時の角処理が正しく適用され、始点と終点が自然につながる。
見た目は閉じているが、パスとしては開いたまま。stroke の角が途切れて見えることがある。
addArc で円弧を描く
addArc は中心座標・半径・開始角度・終了角度を指定して円弧を描く。角度はラジアンで指定し、clockwise パラメータで描画方向を制御する。
Path { path in
path.addArc(
center: CGPoint(x: 100, y: 100),
radius: 80,
startAngle: .degrees(0),
endAngle: .degrees(270),
clockwise: false
)
}
.stroke(.purple, lineWidth: 3)注意すべき点として、SwiftUI の座標系では Y 軸が下向きに増加する。そのため、数学的な反時計回りが画面上では時計回りに見える。clockwise: false が画面上での時計回りの描画になるという、直感に反する挙動をするので覚えておきたい。
addArc の clockwise パラメータは、数学的な座標系(Y 軸上向き)を基準にしている。SwiftUI の画面座標系は Y 軸が下向きなので、clockwise: false を指定すると画面上では時計回りに描画される。
UIKit・SwiftUI ともに同じ挙動。Core Graphics の座標系に由来する仕様。
addArc の実用例:パイチャートのスライス
addArc は円弧を描くだけでなく、扇形を作るのにも使える。中心から弧の始点に move し、addArc で弧を描き、closeSubpath で中心に戻せばスライスの完成だ。
Path { path in
let center = CGPoint(x: 100, y: 100)
path.move(to: center)
path.addArc(
center: center,
radius: 80,
startAngle: .degrees(-90),
endAngle: .degrees(30),
clockwise: false
)
path.closeSubpath()
}
.fill(.green)startAngle を -90 度にしているのは、0 度が 3 時方向にあたるためだ。12 時方向から開始したい場合は -90 度をオフセットとして使うのが定石である。
move(to:) による複数パス
ひとつの Path クロージャ内で move(to:) を複数回呼ぶと、離れた位置に別々の図形を描ける。これをサブパスと呼ぶ。
Path { path in
// 1本目の線
path.move(to: CGPoint(x: 10, y: 50))
path.addLine(to: CGPoint(x: 100, y: 50))
// 2本目の線(離れた位置)
path.move(to: CGPoint(x: 10, y: 100))
path.addLine(to: CGPoint(x: 100, y: 100))
}
.stroke(.gray, lineWidth: 2)グリッド線やメモリ付きの目盛りなど、等間隔に複数の線を描画するときに便利なテクニックだ。ただし、サブパスが増えすぎるとコードの可読性が下がるので、複雑な図形は Shape プロトコルに準拠した独自の型として切り出すほうが管理しやすい。
addLines による一括指定
頂点が多い図形を addLine で一つずつ書くのは冗長になりがちだ。addLines メソッドを使えば、座標の配列を渡して一括で連結できる。
Path { path in
path.move(to: CGPoint(x: 50, y: 10))
path.addLines([
CGPoint(x: 90, y: 40),
CGPoint(x: 80, y: 90),
CGPoint(x: 20, y: 90),
CGPoint(x: 10, y: 40)
])
path.closeSubpath()
}
.stroke(.teal, lineWidth: 2)この例では五角形を描画している。move で始点を指定し、addLines で残りの 4 頂点を一括追加、最後に closeSubpath で閉じるという流れだ。データ駆動で頂点を生成する場合にも addLines は相性がよい。
StrokeStyle でスタイルを調整
Path の stroke には StrokeStyle を渡すことで、線幅以外の細かい描画設定が可能になる。
Path { path in
path.move(to: CGPoint(x: 10, y: 50))
path.addLine(to: CGPoint(x: 250, y: 50))
}
.stroke(
.red,
style: StrokeStyle(
lineWidth: 3,
lineCap: .round,
dash: [10, 5]
)
)線の端の形状を指定する。.butt(デフォルト)は端をそのまま切り落とし、.round は丸く、.square は線幅の半分だけ延長した四角になる。
線が折れ曲がる箇所の形状を指定する。.miter(デフォルト)は鋭角、.round は丸め、.bevel は面取りになる。鋭い角度の頂点では .miter が極端に尖るため、miterLimit と併用することが多い。
破線パターンを配列で指定する。[10, 5] なら 10pt 描画・5pt 空白の繰り返し。配列の要素数を増やせば、長短の混在パターンも表現できる。
Path の基本操作は move・addLine・addArc の 3 つに集約される。これらを組み合わせるだけで直線・多角形・円弧・扇形など多くの図形を描画可能だ。次の記事では、直線や円弧では表現できないなめらかな曲線を描くための addCurve とベジェ曲線について扱う。