CGContext în iOS — ce este, metode și cum să gestionezi contextul de desenare

Autor: IT Sectr Publicat: 2026-07-21 Timp de citire: 9 min

CGContext — obiectul principal Core Graphics pentru executarea comenzilor de desenare 2D în iOS și macOS. Reprezintă un context grafic care stochează starea curentă: culoarea de umplere, grosimea liniei, transformarea, zona de decupare și fonturile. Toate operațiile de desenare — linii, dreptunghiuri, text, imagini — se execută prin CGContext. Conform Apple Developer Documentation, 2026, CGContext este fundamentul întregii grafici raster și vectoriale în ecosistemul Apple.

Principalele

  • CGContext — contextul grafic prin care se execută toate comenzile 2D Core Graphics.
  • setStrokeColor și setFillColor gestionează culoarea liniilor și umplerilor în context.
  • addPath și drawPath permit desenarea de contururi CGPath arbitrare.
  • clip(to:) stabilește zona de decupare pentru a limita desenarea la o anumită zonă.
  • saveGState și restoreGState salvează și restaurează stiva de stări a contextului.

Ce este CGContext?

CGContext — un tip opac (opaque type) din framework-ul Core Graphics, care reprezintă un mediu pentru executarea comenzilor de randare 2D. Acesta gestionează stiva de stări, spațiul de culoare curent, transformarea coordonatelor și zona de decupare. CGContext este singurul obiect cu care interacționează toate funcțiile de desenare Quartz 2D — orice operație grafică trece prin el.

CGContext există în mai multe variante în funcție de scop: bitmap context pentru lucrul cu imagini raster în memorie, PDF context pentru generarea documentelor PDF, window context pentru randarea pe ecran (furnizat de UIView în draw(_:)). În iOS, dezvoltatorii lucrează cel mai des cu contextul obținut din UIGraphicsGetCurrentContext() în interiorul metodei draw(_:) sau din UIGraphicsBeginImageContextWithOptions pentru randare offline.

Contextul aplică toate setările prin stiva de stări — dezvoltatorul poate salva starea curentă (saveGState), modifica parametrii și restaura starea anterioară (restoreGState). Acest lucru este critic la randarea scenelor complexe unde diferite elemente necesită culori, grosimi de linie și transformări diferite. Conform Apple WWDC 2023, utilizarea corectă a stivei de stări îmbunătățește lizibilitatea codului și previne artefactele vizuale.

Gestionarea stării: saveGState și restoreGState

CGContextSaveGState plasează o copie a stării curente a contextului în stivă. Această stare include toate setările: culori, grosimea liniei, transformare, umbre, zona de decupare, fonturi. După modificarea parametrilor, apelul CGContextRestoreGState readuce contextul la starea salvată, anulând toate modificările intermediare. Stiva poate conține până la 32 de stări pe iOS.

Aplicarea practică a stivei

La desenarea unui grup de elemente cu stiluri diferite (de exemplu, sectoarele colorate ale unei diagrame), stiva de stări elimină necesitatea de a reține și restaura manual fiecare parametru. Fără saveGState/restoreGState, dezvoltatorul ar trebui să reseteze culoarea, umbra și transformarea după fiecare element. Utilizarea stivei scurtează codul și elimină erorile de restaurare.

Este important să se mențină echilibrul salvărilor și restaurărilor — fiecare saveGState trebuie să aibă un restoreGState corespunzător. Încălcarea echilibrului duce la scurgeri de stări sau restaurare prematură, ceea ce cauzează afișare imprevizibilă. Analizorul static Xcode nu verifică echilibrul stivei CGContext, astfel că responsabilitatea pentru corectitudine revine dezvoltatorului. Se recomandă gruparea perechilor în blocuri do/finally.

swift
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()
}

Culori și umpleri

CGContextSetFillColorWithColor setează culoarea de umplere pentru operațiile următoare. Culoarea este transmisă ca CGColorRef, care conține informații despre spațiul de culoare și componentele culorii. CGContextSetStrokeColorWithColor funcționează similar pentru culoarea conturului. Apple recomandă stocarea în cache a CGColorRef-urilor frecvent utilizate pentru creșterea performanței.

Gradiente în CGContext

CGContextDrawLinearGradient desenează un gradient liniar între două puncte cu culori specificate. CGContextDrawRadialGradient creează un gradient radial care trece de la un cerc la altul. Pentru a lucra cu gradiente, se creează mai întâi un obiect CGGradientRef care conține o matrice de culori și poziții de oprire. Gradientele în CGContext suportă atât spații de culoare RGB, cât și Grayscale.

Umplerea unei zone cu un gradient necesită setarea prealabilă a zonei de decupare. Fără clip, gradientul va umple întregul context, nu doar forma dorită. Modelul tipic: adăugați o cale în context, apelați clip pentru a limita zona, apoi desenați gradientul. Acest model este utilizat în toate aplicațiile care necesită umplere cu gradient a formelor complexe, de la butoane la fundaluri.

FuncțieScopSpațiu de culoare
setFillColorSetează culoarea de umplereOricare (prin CGColor)
setStrokeColorSetează culoarea conturuluiOricare (prin CGColor)
drawLinearGradientDesenează un gradient liniarRGB sau Grayscale
drawRadialGradientDesenează un gradient radialRGB sau Grayscale

Desenarea contururilor și liniilor

CGContextAddPath adaugă un CGPathRef creat anterior în context pentru randarea ulterioară. După adăugarea căii, aceasta poate fi conturată (strokePath), umplută (fillPath) sau ambele operații. CGContextBeginPath începe o nouă cale, ștergând-o pe cea anterioară. Contextul suportă doar o singură cale activă la un moment dat.

Linii drepte și curbe după puncte

CGContextMoveToPoint mută punctul curent al căii la coordonatele specificate. CGContextAddLineToPoint desenează o linie dreaptă de la punctul curent la cel specificat. CGContextAddCurveToPoint adaugă o curbă cubică Bézier, CGContextAddQuadCurveToPoint — una pătratică. CGContextClosePath închide calea cu o linie dreaptă între ultimul și primul punct.

Spre deosebire de UIBezierPath, unde toate aceste metode sunt apelate pe obiectul căii, în CGContext ele sunt apelate direct pe context. Această diferență este importantă la alegerea abordării: UIBezierPath este mai convenabil pentru reutilizarea contururilor, CGContext — pentru randarea unică cu cheltuieli minime. CGContext suportă, de asemenea, linii punctate prin CGContextSetLineDash, capete flexibile prin CGContextSetLineCap și împreunări prin CGContextSetLineJoin.

swift
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()

Zona de decupare și transformări

CGContextClip limitează zona de desenare la calea curentă. Toate operațiile de desenare ulterioare vor fi vizibile doar în interiorul acestei zone. Acesta este mecanismul de bază pentru mascare, rotunjirea colțurilor și crearea de margini personalizate ale elementelor. Zona de decupare este, de asemenea, salvată și restaurată prin saveGState/restoreGState.

Transformarea coordonatelor

CGContextTranslateCTM deplasează originea coordonatelor contextului. CGContextScaleCTM scalează axele X și Y. CGContextRotateCTM rotește sistemul de coordonate în jurul originii curente. Transformările se cumulează — fiecare transformare ulterioară se aplică relativ la sistemul de coordonate curent. Transformările compuse (deplasare + rotație + scalare) sunt utilizate pentru animație și poziționarea elementelor.

Utilizarea combinată a decupării și transformării oferă posibilități puternice: se poate desena o mască complexă prin decupare, apoi aplica o transformare întregului conținut. Un exemplu tipic — afișarea unei părți dintr-o imagine sub un unghi cu margini rotunjite. Ordinea operațiilor este importantă: mai întâi setați zona de decupare, apoi aplicați transformarea, apoi desenați conținutul. Apple recomandă minimizarea numărului de modificări CTM (Current Transformation Matrix) în bucla de randare.

swift
override func draw(_ rect: CGRect) {
    guard let ctx = UIGraphicsGetCurrentContext() else { return }
    ctx.saveGState()

    // zonă de decupare — cerc
    ctx.addEllipse(in: CGRect(x: 50, y: 50, width: 200, height: 200))
    ctx.clip()

    // transformare conținut
    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()
}

Exemplu de desenare în CGContext

Să analizăm un exemplu complet de creare a unei diagrame cu gradient folosind CGContext. Diagrama include trei sectoare de culori diferite cu umplere gradient și etichete text. Codul demonstrează o combinație de saveGState, clip, gradiente și randare text în același context.

swift
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()

            // etichetă text
            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
        }
    }
}

În acest exemplu, fiecare sector al diagramei este desenat în propriul bloc saveGState/restoreGState. Această abordare garantează că setările de culoare și cale pentru un sector nu influențează celelalte. Metoda addArc creează un arc de la unghiul de început la cel de sfârșit, iar closePath închide sectorul spre centru. Eticheta text este plasată pe linia mediană a unghiului sectorului folosind funcțiile trigonometrice cos și sin pentru calcularea poziției.

Pentru a crește performanța la redesenarea frecventă, se recomandă stocarea în cache a calculelor unghiurilor și pozițiilor într-un tablou separat. CGContext execută toate operațiile pe CPU, prin urmare scenele complexe cu zeci de sectoare pot necesita optimizare — de exemplu, rasterizarea prealabilă în CGLayer sau utilizarea Metal pentru accelerare GPU. Pentru majoritatea sarcinilor UI cu 5–10 elemente, performanța CGContext rămâne acceptabilă.

Întrebări frecvente

Cum obțin CGContext-ul curent în iOS?

Apelați UIGraphicsGetCurrentContext() în interiorul metodei draw(_:) a UIView sau în interiorul blocului UIGraphicsBeginImageContextWithOptions. Pentru straturile CALayer, utilizați draw(in:) al delegatului CALayerDelegate, care primește un context gata pregătit.

Care este diferența dintre fillPath și strokePath?

fillPath umple zona interioară a căii curente cu culoarea din setFillColor. strokePath desenează o linie de-a lungul căii cu parametrii setStrokeColor, setLineWidth și setLineCap. Ambele operații se execută secvențial: mai întâi fill, apoi stroke dacă este necesar.

Ce este bitmap CGContext?

Bitmap CGContext — un context creat prin CGBitmapContextCreate care desenează într-un buffer de memorie în loc de ecran. Este utilizat pentru generarea de imagini în fundal, procesarea graficii fără afișare și crearea de texturi pentru Metal sau OpenGL.

Cum resetez toate setările CGContext?

Core Graphics nu oferă o metodă de resetare a tuturor setărilor. Utilizați saveGState înainte de modificări și restoreGState pentru a reveni la starea inițială. Alternativ, puteți crea un context nou cu aceiași parametri.

De ce gradientul nu se afișează în interiorul formei?

Înainte de a apela drawLinearGradient, setați zona de decupare prin clip pe calea dorită. Fără clip, gradientul umple întregul context. Ordinea: addPath → clip → drawLinearGradient — garantează gradientul în interiorul formei.

Rezumat

  • CGContext — obiectul central Core Graphics pentru toate operațiile de desenare 2D pe iOS și macOS.
  • saveGState și restoreGState gestionează stiva de stări a contextului, prevenind efectele secundare.
  • setFillColor și setStrokeColor stabilesc culorile de umplere și contur prin CGColorRef.
  • addPath, strokePath, fillPath lucrează cu contururi CGPath arbitrare.
  • clip limitează zona de desenare la calea curentă pentru măști și rotunjiri.
  • translateCTM, scaleCTM, rotateCTM transformă sistemul de coordonate al contextului.
  • Toate operațiile CGContext se execută pe CPU — pentru grafică complexă, utilizați CGLayer sau Metal.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și