CGContext è l'oggetto principale di Core Graphics per eseguire comandi di disegno 2D in iOS e macOS. Rappresenta un contesto grafico che memorizza lo stato corrente: colore di riempimento, spessore della linea, trasformazione, area di ritaglio e font. Tutte le operazioni di disegno — linee, rettangoli, testo, immagini — vengono eseguite attraverso CGContext. Secondo Apple Developer Documentation, 2026, CGContext è il fondamento di tutta la grafica raster e vettoriale nell'ecosistema Apple.
Punti Chiave
CGContext è un tipo opaco (opaque type) del framework Core Graphics che rappresenta un ambiente per eseguire comandi di rendering 2D. Gestisce lo stack degli stati, lo spazio colore corrente, la trasformazione delle coordinate e l'area di ritaglio. CGContext è l'unico oggetto con cui interagiscono tutte le funzioni di disegno Quartz 2D — qualsiasi operazione grafica passa attraverso di esso.
CGContext esiste in diverse varietà a seconda dello scopo: bitmap context per lavorare con immagini raster in memoria, PDF context per generare documenti PDF, window context per il disegno su schermo (fornito da UIView in draw(_:)). Gli sviluppatori iOS lavorano più spesso con un contesto ottenuto da UIGraphicsGetCurrentContext() all'interno del metodo draw(_:) o da UIGraphicsBeginImageContextWithOptions per il rendering offline.
Il contesto applica tutte le impostazioni attraverso uno stack di stati — lo sviluppatore può salvare lo stato corrente (saveGState), modificare i parametri e ripristinare lo stato precedente (restoreGState). Questo è di fondamentale importanza quando si disegnano scene complesse dove elementi diversi richiedono colori, spessori di linea e trasformazioni differenti. Secondo Apple WWDC 2023, l'uso corretto dello stack di stati migliora la leggibilità del codice e previene artefatti visivi.
CGContextSaveGState inserisce una copia dello stato corrente del contesto nello stack. Questo stato include tutte le impostazioni: colori, spessore della linea, trasformazione, ombre, area di ritaglio, font. Dopo aver modificato i parametri, la chiamata a CGContextRestoreGState riporta il contesto allo stato salvato, annullando tutte le modifiche intermedie. Lo stack può contenere fino a 32 stati su iOS.
Quando si disegnano gruppi di elementi con stili diversi (ad esempio, settori di un grafico multicolore), lo stack di stati elimina la necessità di ricordare e ripristinare manualmente ogni parametro. Senza saveGState/restoreGState, lo sviluppatore dovrebbe reimpostare colore, ombra e trasformazione dopo ogni elemento. Usare lo stack riduce il codice e previene errori di ripristino.
È importante mantenere un equilibrio di salvataggi e ripristini — ogni saveGState deve avere un restoreGState corrispondente. Rompere l'equilibrio causa perdite di stato o ripristino prematuro, con conseguente rendering imprevedibile. L'analizzatore statico di Xcode non verifica l'equilibrio dello stack di CGContext, quindi la correttezza è responsabilità dello sviluppatore. Si consiglia di raggruppare le coppie in blocchi 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 imposta il colore di riempimento per le operazioni successive. Il colore viene passato come CGColorRef, che contiene informazioni sullo spazio colore e sui componenti del colore. CGContextSetStrokeColorWithColor funziona in modo simile per il colore del tratto. Apple raccomanda di memorizzare nella cache i valori CGColorRef usati frequentemente per migliorare le prestazioni.
CGContextDrawLinearGradient disegna un gradiente lineare tra due punti con colori specificati. CGContextDrawRadialGradient crea un gradiente radiale che passa da un cerchio all'altro. Per lavorare con i gradienti, viene prima creato un oggetto CGGradientRef contenente un array di colori e posizioni di arresto. I gradienti in CGContext supportano sia gli spazi colore RGB che Scala di Grigi.
Riempire un'area con un gradiente richiede prima di impostare un'area di ritaglio. Senza clip, il gradiente riempirà l'intero contesto, non solo la forma desiderata. Il pattern tipico: aggiungere un percorso al contesto, chiamare clip per limitare l'area, quindi disegnare il gradiente. Questo pattern viene utilizzato in tutte le applicazioni che richiedono riempimenti gradienti di forme complesse, dai pulsanti agli sfondi.
| Funzione | Scopo | Spazio Colore |
|---|---|---|
| setFillColor | Imposta il colore di riempimento | Qualsiasi (tramite CGColor) |
| setStrokeColor | Imposta il colore del tratto | Qualsiasi (tramite CGColor) |
| drawLinearGradient | Disegna un gradiente lineare | RGB o Scala di Grigi |
| drawRadialGradient | Disegna un gradiente radiale | RGB o Scala di Grigi |
CGContextAddPath aggiunge un CGPathRef precedentemente creato al contesto per il rendering successivo. Dopo aver aggiunto un percorso, può essere tracciato (strokePath), riempito (fillPath) o entrambi. CGContextBeginPath inizia un nuovo percorso, cancellando il precedente. Il contesto supporta un solo percorso attivo alla volta.
CGContextMoveToPoint sposta il punto corrente del contorno alle coordinate specificate. CGContextAddLineToPoint disegna una linea retta dal punto corrente al punto dato. CGContextAddCurveToPoint aggiunge una curva cubica di Bézier, CGContextAddQuadCurveToPoint aggiunge una quadratica. CGContextClosePath chiude il contorno con una linea retta tra l'ultimo e il primo punto.
A differenza di UIBezierPath, dove tutti questi metodi vengono chiamati sull'oggetto percorso, in CGContext vengono chiamati direttamente sul contesto. Questa differenza è importante nella scelta dell'approccio: UIBezierPath è più conveniente per riutilizzare i percorsi, CGContext è migliore per il disegno singolo con overhead minimo. CGContext supporta anche linee tratteggiate tramite CGContextSetLineDash, estremità flessibili tramite CGContextSetLineCap e giunzioni tramite 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 limita l'area di disegno al percorso corrente. Tutte le operazioni di disegno successive saranno visibili solo all'interno di quest'area. Questo è il meccanismo di base per mascheratura, arrotondamento degli angoli e creazione di confini personalizzati degli elementi. L'area di ritaglio viene anche salvata e ripristinata tramite saveGState/restoreGState.
CGContextTranslateCTM sposta l'origine del sistema di coordinate del contesto. CGContextScaleCTM ridimensiona gli assi X e Y. CGContextRotateCTM ruota il sistema di coordinate attorno all'origine corrente. Le trasformazioni si accumulano — ciascuna successiva viene applicata rispetto al sistema di coordinate corrente. Le trasformazioni composte (traslazione + rotazione + scala) vengono utilizzate per animazione e posizionamento degli elementi.
Combinare ritaglio e trasformazioni offre potenti capacità: è possibile disegnare una maschera complessa tramite ritaglio, quindi applicare una trasformazione all'intero contenuto. Un esempio tipico è la visualizzazione di una parte di immagine in angolo con angoli arrotondati. L'ordine delle operazioni è importante: prima impostare l'area di ritaglio, poi applicare la trasformazione, quindi disegnare il contenuto. Apple raccomanda di minimizzare le modifiche alla CTM (Matrice di Trasformazione Corrente) nel ciclo di disegno.
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()
}
Vediamo un esempio completo di creazione di un grafico gradiente utilizzando CGContext. Il grafico include tre settori di diversi colori con riempimenti gradienti ed etichette di testo. Il codice dimostra la combinazione di saveGState, clip, gradienti e rendering del testo in un singolo contesto.
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
}
}
}
In questo esempio, ogni settore del grafico viene disegnato nel proprio blocco saveGState/restoreGState. Questo approccio garantisce che le impostazioni di colore e percorso di un settore non influenzino gli altri. Il metodo addArc crea un arco dall'angolo iniziale a quello finale, e closePath chiude il settore verso il centro. Il testo dell'etichetta viene posizionato all'angolo medio del settore utilizzando le funzioni trigonometriche cos e sin per il calcolo della posizione.
Per migliorare le prestazioni durante il ridisegno frequente, si consiglia di memorizzare nella cache i calcoli di angoli e posizioni in un array separato. CGContext esegue tutte le operazioni sulla CPU, quindi scene complesse con dozzine di settori possono richiedere ottimizzazione — ad esempio, pre-rendering in CGLayer o l'uso di Metal per l'accelerazione GPU. Per la maggior parte delle attività UI con 5–10 elementi, le prestazioni di CGContext rimangono accettabili.
Domande Frequenti
Chiama UIGraphicsGetCurrentContext() all'interno del metodo draw(_:) di UIView o all'interno di un blocco UIGraphicsBeginImageContextWithOptions. Per i livelli CALayer, usa il metodo draw(in:) di CALayerDelegate, che riceve un contesto pronto.
fillPath riempie l'interno del percorso corrente con il colore di setFillColor. strokePath disegna una linea lungo il percorso con i parametri di setStrokeColor, setLineWidth e setLineCap. Entrambe le operazioni vengono eseguite sequenzialmente: prima il riempimento, poi il tratto se necessario.
Bitmap CGContext è un contesto creato tramite CGBitmapContextCreate che disegna in un buffer di memoria invece che sullo schermo. Viene utilizzato per la generazione di immagini in background, l'elaborazione grafica senza visualizzazione e la creazione di texture per Metal o OpenGL.
Core Graphics non fornisce un metodo per resettare tutte le impostazioni. Usa saveGState prima delle modifiche e restoreGState per tornare allo stato originale. In alternativa, crea un nuovo contesto con gli stessi parametri.
Prima di chiamare drawLinearGradient, imposta l'area di ritaglio tramite clip sul percorso desiderato. Senza clip, il gradiente riempie l'intero contesto. Ordine: addPath → clip → drawLinearGradient — garantisce il gradiente all'interno della forma.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche