CGContext w iOS — co to jest, metody i jak zarządzać kontekstem rysowania

Autor: IT Sectr Opublikowano: 2026-07-21 Czas czytania: 9 min

CGContext — podstawowy obiekt Core Graphics do wykonywania poleceń rysowania 2D w iOS i macOS. Reprezentuje kontekst graficzny, który przechowuje bieżący stan: kolor wypełnienia, grubość linii, transformację, obszar przycinania i czcionki. Wszystkie operacje rysowania — linie, prostokąty, tekst, obrazy — są wykonywane przez CGContext. Według Apple Developer Documentation, 2026, CGContext jest fundamentem całej grafiki rastrowej i wektorowej w ekosystemie Apple.

Najważniejsze

  • CGContext — kontekst graficzny, przez który wykonywane są wszystkie polecenia 2D Core Graphics.
  • setStrokeColor i setFillColor zarządzają kolorem linii i wypełnień w kontekście.
  • addPath i drawPath pozwalają rysować dowolne kontury CGPath.
  • clip(to:) ustawia obszar przycinania do ograniczenia rysowania do określonej strefy.
  • saveGState i restoreGState zapisują i przywracają stos stanów kontekstu.

Co to jest CGContext?

CGContext — nieprzezroczysty typ (opaque type) z frameworka Core Graphics, reprezentujący środowisko do wykonywania poleceń renderowania 2D. Zarządza stosem stanów, bieżącą przestrzenią kolorów, transformacją współrzędnych i obszarem przycinania. CGContext jest jedynym obiektem, z którym współdziałają wszystkie funkcje rysowania Quartz 2D — przez niego przechodzą wszystkie operacje graficzne.

CGContext występuje w kilku odmianach w zależności od przeznaczenia: bitmap context do pracy z obrazami rastrowymi w pamięci, PDF context do generowania dokumentów PDF, window context do renderowania na ekranie (dostarczany przez UIView w draw(_:)). W iOS programiści najczęściej pracują z kontekstem uzyskanym z UIGraphicsGetCurrentContext() wewnątrz metody draw(_:) lub z UIGraphicsBeginImageContextWithOptions do renderowania offline.

Kontekst stosuje wszystkie ustawienia przez stos stanów — programista może zapisać bieżący stan (saveGState), zmienić parametry i przywrócić poprzedni (restoreGState). Jest to krytyczne przy renderowaniu złożonych scen, gdzie różne elementy wymagają różnych kolorów, grubości linii i transformacji. Według Apple WWDC 2023, prawidłowe używanie stosu stanów zwiększa czytelność kodu i zapobiega artefaktom wizualnym.

Zarządzanie stanem: saveGState i restoreGState

CGContextSaveGState umieszcza kopię bieżącego stanu kontekstu na stosie. Ten stan obejmuje wszystkie ustawienia: kolory, grubość linii, transformację, cienie, obszar przycinania, czcionki. Po modyfikacji parametrów wywołanie CGContextRestoreGState przywraca kontekst do zapisanego stanu, odwracając wszystkie pośrednie zmiany. Stos może zawierać do 32 stanów w iOS.

Praktyczne zastosowanie stosu

Podczas rysowania grupy elementów z różnymi stylami (na przykład różnokolorowe sektory wykresu) stos stanów eliminuje konieczność ręcznego zapamiętywania i przywracania każdego parametru. Bez saveGState/restoreGState programista musiałby ponownie ustawiać kolor, cień i transformację po każdym elemencie. Użycie stosu skraca kod i eliminuje błędy przywracania.

Ważne jest zachowanie równowagi zapisów i przywracania — każdy saveGState musi mieć odpowiadający restoreGState. Naruszenie równowagi prowadzi do wycieków stanów lub przedwczesnego przywracania, co powoduje nieprzewidywalne wyświetlanie. Statyczny analizator Xcode nie sprawdza równowagi stosu CGContext, więc odpowiedzialność za poprawność leży po stronie programisty. Zaleca się grupowanie par w blokach 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()
}

Kolory i wypełnienia

CGContextSetFillColorWithColor ustawia kolor wypełnienia dla kolejnych operacji. Kolor jest przekazywany jako CGColorRef, który zawiera informacje o przestrzeni kolorów i składowych koloru. CGContextSetStrokeColorWithColor działa analogicznie dla koloru obrysu. Apple zaleca buforowanie często używanych CGColorRef w celu zwiększenia wydajności.

Gradienty w CGContext

CGContextDrawLinearGradient rysuje gradient liniowy między dwoma punktami z określonymi kolorami. CGContextDrawRadialGradient tworzy gradient promienisty, przechodzący z jednego okręgu do drugiego. Do pracy z gradientami najpierw tworzy się obiekt CGGradientRef zawierający tablicę kolorów i pozycji zatrzymań. Gradienty w CGContext obsługują przestrzenie kolorów RGB i Grayscale.

Wypełnienie obszaru gradientem wymaga wcześniejszego ustawienia obszaru przycinania. Bez clip gradient wypełni cały kontekst, a nie tylko potrzebny kształt. Typowy wzorzec: dodać ścieżkę do kontekstu, wywołać clip w celu ograniczenia obszaru, a następnie narysować gradient. Ten wzorzec jest używany we wszystkich aplikacjach wymagających gradientowego wypełnienia złożonych kształtów, od przycisków po tła.

FunkcjaPrzeznaczeniePrzestrzeń kolorów
setFillColorUstawia kolor wypełnieniaDowolna (przez CGColor)
setStrokeColorUstawia kolor obrysuDowolna (przez CGColor)
drawLinearGradientRysuje gradient liniowyRGB lub Grayscale
drawRadialGradientRysuje gradient promienistyRGB lub Grayscale

Rysowanie konturów i linii

CGContextAddPath dodaje wcześniej utworzony CGPathRef do kontekstu w celu późniejszego renderowania. Po dodaniu ścieżki można ją obrysować (strokePath), wypełnić (fillPath) lub wykonać obie operacje. CGContextBeginPath rozpoczyna nową ścieżkę, czyszcząc poprzednią. Kontekst obsługuje tylko jedną aktywną ścieżkę w danym momencie.

Linie proste i krzywe po punktach

CGContextMoveToPoint przenosi bieżący punkt ścieżki do określonych współrzędnych. CGContextAddLineToPoint rysuje prostą linię od bieżącego punktu do zadanego. CGContextAddCurveToPoint dodaje krzywą Cubic Béziera, CGContextAddQuadCurveToPoint — kwadratową. CGContextClosePath zamyka ścieżkę prostą linią między ostatnim i pierwszym punktem.

W przeciwieństwie do UIBezierPath, gdzie wszystkie te metody są wywoływane na obiekcie ścieżki, w CGContext są one wywoływane bezpośrednio na kontekście. Ta różnica jest ważna przy wyborze podejścia: UIBezierPath jest wygodniejszy do wielokrotnego użycia konturów, CGContext — do jednorazowego renderowania z minimalnym narzutem. CGContext obsługuje również linie przerywane przez CGContextSetLineDash, elastyczne zakończenia przez CGContextSetLineCap i łączenia przez 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()

Obszar przycinania i transformacje

CGContextClip ogranicza obszar rysowania do bieżącej ścieżki. Wszystkie kolejne operacje rysowania będą widoczne tylko wewnątrz tego obszaru. Jest to podstawowy mechanizm maskowania, zaokrąglania rogów i tworzenia niestandardowych granic elementów. Obszar przycinania jest również zapisywany i przywracany przez saveGState/restoreGState.

Transformacja współrzędnych

CGContextTranslateCTM przesuwa początek układu współrzędnych kontekstu. CGContextScaleCTM skaluje osie X i Y. CGContextRotateCTM obraca układ współrzędnych wokół bieżącego początku. Transformacje kumulują się — każda kolejna jest stosowana względem bieżącego układu współrzędnych. Złożone transformacje (przesunięcie + obrót + skalowanie) są używane do animacji i pozycjonowania elementów.

Połączenie przycinania i transformacji daje potężne możliwości: można narysować złożoną maskę przez przycięcie, a następnie zastosować transformację do całej zawartości. Typowy przykład — wyświetlanie części obrazu pod kątem z zaokrąglonymi krawędziami. Kolejność operacji jest ważna: najpierw ustaw obszar przycinania, potem zastosuj transformację, następnie rysuj zawartość. Apple zaleca minimalizowanie liczby zmian CTM (Current Transformation Matrix) w pętli renderowania.

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

    // region przycinania — okrąg
    ctx.addEllipse(in: CGRect(x: 50, y: 50, width: 200, height: 200))
    ctx.clip()

    // transformacja treści
    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()
}

Przykład rysowania w CGContext

Rozważmy pełny przykład tworzenia gradientowego wykresu kołowego z użyciem CGContext. Wykres obejmuje trzy sektory w różnych kolorach z gradientowym wypełnieniem i podpisami tekstowymi. Kod demonstruje kombinację saveGState, clip, gradientów i renderowania tekstu w jednym kontekście.

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

            // etykieta tekstowa
            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
        }
    }
}

W tym przykładzie każdy sektor wykresu jest rysowany we własnym bloku saveGState/restoreGState. Takie podejście gwarantuje, że ustawienia koloru i ścieżki dla jednego sektora nie wpływają na inne. Metoda addArc tworzy łuk od kąta początkowego do końcowego, closePath zamyka sektor do środka. Podpis tekstowy jest umieszczany na linii środkowej kąta sektora z użyciem funkcji trygonometrycznych cos i sin do obliczenia pozycji.

Aby zwiększyć wydajność przy częstym przerysowywaniu, zaleca się buforowanie obliczeń kątów i pozycji w osobnej tablicy. CGContext wykonuje wszystkie operacje na CPU, dlatego złożone sceny z dziesiątkami sektorów mogą wymagać optymalizacji — na przykład wstępnej rasteryzacji w CGLayer lub użycia Metal do akceleracji GPU. Dla większości zadań UI z 5–10 elementami wydajność CGContext pozostaje akceptowalna.

Często zadawane pytania

Jak uzyskać bieżący CGContext w iOS?

Wywołaj UIGraphicsGetCurrentContext() wewnątrz metody draw(_:) UIView lub wewnątrz bloku UIGraphicsBeginImageContextWithOptions. Dla warstw CALayer użyj draw(in:) delegata CALayerDelegate, który otrzymuje gotowy kontekst.

Jaka jest różnica między fillPath a strokePath?

fillPath wypełnia wewnętrzny obszar bieżącej ścieżki kolorem z setFillColor. strokePath rysuje linię wzdłuż ścieżki z parametrami setStrokeColor, setLineWidth i setLineCap. Obie operacje są wykonywane sekwencyjnie: najpierw fill, potem stroke w razie potrzeby.

Czym jest bitmap CGContext?

Bitmap CGContext — kontekst utworzony przez CGBitmapContextCreate, który rysuje do bufora pamięci zamiast na ekran. Używany do tłowej generacji obrazów, przetwarzania grafiki bez wyświetlania i tworzenia tekstur dla Metal lub OpenGL.

Jak zresetować wszystkie ustawienia CGContext?

Core Graphics nie udostępnia metody resetowania wszystkich ustawień. Użyj saveGState przed zmianami i restoreGState do powrotu do stanu początkowego. Alternatywnie można utworzyć nowy kontekst z tymi samymi parametrami.

Dlaczego gradient nie wyświetla się wewnątrz kształtu?

Przed wywołaniem drawLinearGradient ustaw obszar przycinania przez clip na odpowiednią ścieżkę. Bez clip gradient wypełnia cały kontekst. Kolejność: addPath → clip → drawLinearGradient — gwarantuje gradient wewnątrz kształtu.

Podsumowanie

  • CGContext — centralny obiekt Core Graphics dla wszystkich operacji rysowania 2D w iOS i macOS.
  • saveGState i restoreGState zarządzają stosem stanów kontekstu, zapobiegając efektom ubocznym.
  • setFillColor i setStrokeColor ustawiają kolory wypełnienia i obrysu przez CGColorRef.
  • addPath, strokePath, fillPath pracują z dowolnymi konturami CGPath.
  • clip ogranicza obszar rysowania do bieżącej ścieżki dla masek i zaokrągleń.
  • translateCTM, scaleCTM, rotateCTM transformują układ współrzędnych kontekstu.
  • Wszystkie operacje CGContext są wykonywane na CPU — dla złożonej grafiki użyj CGLayer lub Metal.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również