CGContextは、iOSおよびmacOSで2D描画コマンドを実行するためのCore Graphicsの主要オブジェクトです。これは、現在の状態(塗りつぶし色、線の太さ、変換、クリッピング領域、フォント)を保存するグラフィックコンテキストを表します。線、長方形、テキスト、画像など、すべての描画操作はCGContextを通じて実行されます。Apple Developer Documentation, 2026によると、CGContextはAppleエコシステムにおけるすべてのラスターおよびベクターグラフィックスの基盤です。
重要なポイント
CGContextは、Core Graphicsフレームワークの不透明型(opaque type)で、2Dレンダリングコマンドを実行する環境を表します。状態スタック、現在の色空間、座標変換、クリッピング領域を管理します。CGContextは、すべてのQuartz 2D描画関数が相互作用する唯一のオブジェクトであり、あらゆるグラフィック操作はこれを通過します。
CGContextには、目的に応じていくつかの種類があります。bitmap contextはメモリ内のラスター画像を操作するため、PDF contextはPDFドキュメントを生成するため、window contextは画面への描画(UIViewがdraw(_:)で提供)に使用されます。iOS開発者は、draw(_:)メソッド内のUIGraphicsGetCurrentContext()から、またはオフラインレンダリング用のUIGraphicsBeginImageContextWithOptionsから取得したコンテキストを最も頻繁に使用します。
コンテキストは状態スタックを通じてすべての設定を適用します。開発者は現在の状態を保存し(saveGState)、パラメータを変更し、以前の状態を復元(restoreGState)できます。これは、異なる要素に異なる色、線の太さ、変換が必要な複雑なシーンを描画する際に極めて重要です。Apple WWDC 2023によると、状態スタックの適切な使用はコードの可読性を向上させ、視覚的なアーティファクトを防ぎます。
CGContextSaveGStateは、現在のコンテキスト状態のコピーをスタックにプッシュします。この状態には、色、線の太さ、変換、影、クリッピング領域、フォントなど、すべての設定が含まれます。パラメータを変更した後、CGContextRestoreGStateを呼び出すと、コンテキストは保存された状態に戻り、すべての中間変更が元に戻されます。スタックはiOSで最大32の状態を保持できます。
異なるスタイルの要素グループ(例えば、多色のチャートセクター)を描画する場合、状態スタックは各パラメータを手動で記憶して復元する必要性を排除します。saveGState/restoreGStateがない場合、開発者は各要素の後に色、影、変換をリセットする必要があります。スタックの使用はコードを削減し、復元エラーを防ぎます。
保存と復元のバランスを維持することが重要です。各saveGStateには対応するrestoreGStateが必要です。バランスを崩すと、状態リークや早期復元が発生し、予測不能なレンダリングを引き起こします。Xcodeの静的アナライザはCGContextのスタックバランスをチェックしないため、正確性は開発者の責任です。do/finallyブロックでペアをグループ化することをお勧めします。
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
ctx.setStrokeColor(UIColor.red.cgColor)
ctx.setLineWidth(3)
ctx.addRect(CGRect(x: 50, y: 50, width: 200, height: 100))
ctx.strokePath()
ctx.restoreGState()
}
CGContextSetFillColorWithColorは、後続の操作の塗りつぶし色を設定します。色はCGColorRefとして渡され、色空間と色成分に関する情報を含みます。CGContextSetStrokeColorWithColorは、ストローク色について同様に機能します。Appleは、パフォーマンス向上のために頻繁に使用するCGColorRef値をキャッシュすることを推奨しています。
CGContextDrawLinearGradientは、指定された色で2点間の線形グラデーションを描画します。CGContextDrawRadialGradientは、ある円から別の円に移行する放射状グラデーションを作成します。グラデーションを操作するには、まず色とストップ位置の配列を含むCGGradientRefオブジェクトを作成します。CGContextのグラデーションは、RGBとグレースケールの両方の色空間をサポートします。
グラデーションで領域を塗りつぶすには、最初にクリッピング領域を設定する必要があります。clipがない場合、グラデーションは目的の形状だけでなく、コンテキスト全体を塗りつぶします。典型的なパターン: コンテキストにパスを追加し、領域を制限するためにclipを呼び出し、次にグラデーションを描画します。このパターンは、ボタンから背景まで、複雑な形状のグラデーション塗りつぶしを必要とするすべてのアプリケーションで使用されます。
| 関数 | 目的 | 色空間 |
|---|---|---|
| setFillColor | 塗りつぶし色を設定 | 任意(CGColor経由) |
| setStrokeColor | ストローク色を設定 | 任意(CGColor経由) |
| drawLinearGradient | 線形グラデーションを描画 | RGBまたはグレースケール |
| drawRadialGradient | 放射状グラデーションを描画 | RGBまたはグレースケール |
CGContextAddPathは、後続のレンダリングのために事前に作成されたCGPathRefをコンテキストに追加します。パスを追加した後、ストローク(strokePath)、塗りつぶし(fillPath)、またはその両方ができます。CGContextBeginPathは、前のパスをクリアして新しいパスを開始します。コンテキストは一度に1つのアクティブなパスのみをサポートします。
CGContextMoveToPointは、現在の輪郭点を指定された座標に移動します。CGContextAddLineToPointは、現在の点から指定された点まで直線を描画します。CGContextAddCurveToPointは3次ベジェ曲線を追加し、CGContextAddQuadCurveToPointは2次曲線を追加します。CGContextClosePathは、最後の点と最初の点の間を直線で結んで輪郭を閉じます。
UIBezierPathではこれらのメソッドがすべてパスオブジェクトで呼び出されるのに対し、CGContextではコンテキスト上で直接呼び出されます。この違いはアプローチを選ぶ際に重要です。UIBezierPathはパスの再利用に便利で、CGContextは最小限のオーバーヘッドで1回限りの描画に適しています。CGContextはまた、CGContextSetLineDashによる破線、CGContextSetLineCapによる柔軟な線端、CGContextSetLineJoinによる結合をサポートします。
let ctx = UIGraphicsGetCurrentContext()!
ctx.beginPath()
ctx.moveTo(x: 50, y: 100)
ctx.addLineTo(x: 200, y: 100)
ctx.addCurveTo(x1: 250, y1: 50, x2: 300, y2: 150, x3: 350, y3: 100)
ctx.setStrokeColor(UIColor.blue.cgColor)
ctx.setLineWidth(5)
ctx.strokePath()
CGContextClipは、描画領域を現在のパスに制限します。以降のすべての描画操作は、この領域内でのみ表示されます。これは、マスキング、角丸め、要素のカスタム境界作成の基本メカニズムです。クリッピング領域はsaveGState/restoreGStateを介して保存および復元されます。
CGContextTranslateCTMは、コンテキストの座標系の原点を移動します。CGContextScaleCTMはX軸とY軸をスケーリングします。CGContextRotateCTMは、現在の原点を中心に座標系を回転します。変換は累積され、後続の各変換は現在の座標系に対して適用されます。複合変換(移動+回転+スケール)は、アニメーションと要素の位置決めに使用されます。
クリッピングと変換を組み合わせることで強力な機能が得られます。クリッピングで複雑なマスクを描画し、その後コンテンツ全体に変換を適用できます。典型的な例は、角丸の画像を角度付きで表示することです。操作の順序が重要です。最初にクリッピング領域を設定し、次に変換を適用し、その後コンテンツを描画します。Appleは、描画ループ内のCTM(現在の変換行列)の変更を最小限にすることを推奨しています。
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
// clipping region - circle
ctx.addEllipse(in: CGRect(x: 50, y: 50, width: 200, height: 200))
ctx.clip()
// content transformation
ctx.translateBy(x: 150, y: 150)
ctx.rotate(by: .pi / 4)
ctx.translateBy(x: -150, y: -150)
UIImage(named: "photo")?.draw(at: CGPoint(x: 50, y: 50))
ctx.restoreGState()
}
CGContextを使用したグラデーションチャート作成の完全な例を見てみましょう。このチャートには、グラデーション塗りつぶしとテキストラベルを持つ3つの異なる色のセクターが含まれています。このコードは、単一のコンテキストでのsaveGState、clip、グラデーション、テキストレンダリングの組み合わせを示しています。
class PieChartView: UIView {
private struct Slice {
let color: UIColor
let value: CGFloat
let label: String
}
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
let slices = [
Slice(color: .systemRed, value: 0.4, label: "iOS"),
Slice(color: .systemGreen, value: 0.35, label: "Android"),
Slice(color: .systemBlue, value: 0.25, label: "Other")
]
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = bounds.width * 0.35
var startAngle: CGFloat = -.pi / 2
for slice in slices {
let endAngle = startAngle + .pi * 2 * slice.value
ctx.saveGState()
ctx.moveTo(x: center.x, y: center.y)
ctx.addArc(center: center, radius: radius,
startAngle: startAngle, endAngle: endAngle, clockwise: false)
ctx.closePath()
ctx.setFillColor(slice.color.cgColor)
ctx.fillPath()
// text label
let midAngle = startAngle + endAngle / 2
let labelPos = CGPoint(
x: center.x + radius * 0.7 * cos(midAngle),
y: center.y + radius * 0.7 * sin(midAngle)
)
slice.label.draw(at: labelPos, withAttributes: [
.foregroundColor: UIColor.white,
.font: UIFont.boldSystemFont(ofSize: 14)
])
ctx.restoreGState()
startAngle = endAngle
}
}
}
この例では、チャートの各セクターは独自のsaveGState/restoreGStateブロックで描画されます。このアプローチにより、あるセクターの色とパスの設定が他のセクターに影響を与えないことが保証されます。addArcメソッドは開始角度から終了角度までの円弧を作成し、closePathはセクターを中心に向かって閉じます。ラベルテキストは、三角関数cosとsinを使用して位置を計算し、セクターの角度の中間点に配置されます。
頻繁な再描画時のパフォーマンスを向上させるには、角度と位置の計算を別の配列にキャッシュすることをお勧めします。CGContextはすべての操作をCPUで実行するため、数十のセクターを持つ複雑なシーンでは最適化が必要になる場合があります(例:CGLayerでのプリレンダリングやGPUアクセラレーションのためのMetalの使用)。5~10要素のほとんどのUIタスクでは、CGContextのパフォーマンスは許容範囲内です。
よくある質問
UIViewのdraw(_:)メソッド内、またはUIGraphicsBeginImageContextWithOptionsブロック内でUIGraphicsGetCurrentContext()を呼び出します。CALayerレイヤーの場合は、CALayerDelegateのdraw(in:)メソッドを使用します。これにより準備済みのコンテキストが取得されます。
fillPathは、現在のパスの内部をsetFillColorの色で塗りつぶします。strokePathは、setStrokeColor、setLineWidth、setLineCapのパラメータでパスに沿って線を描画します。両方の操作は順次実行されます。最初に塗りつぶし、必要に応じてストロークが行われます。
Bitmap CGContextは、CGBitmapContextCreateを介して作成されたコンテキストで、画面ではなくメモリバッファに描画します。バックグラウンドでの画像生成、表示なしのグラフィックス処理、MetalやOpenGL用のテクスチャ作成に使用されます。
Core Graphicsはすべての設定をリセットするメソッドを提供していません。変更前にsaveGStateを使用し、元の状態に戻すためにrestoreGStateを使用します。代替として、同じパラメータで新しいコンテキストを作成します。
drawLinearGradientを呼び出す前に、目的のパスでclipを使用してクリッピング領域を設定します。clipがないと、グラデーションはコンテキスト全体を塗りつぶします。順序: addPath → clip → drawLinearGradientにより、図形内にグラデーションが保証されます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。