CGContext в iOS — що це, методи та як керувати контекстом малювання

Автор: IT Sectr Опубліковано: 2026-07-21 Час читання: 9 хв

CGContext — основний об'єкт Core Graphics для виконання 2D-команд малювання в iOS та macOS. Він являє собою графічний контекст, який зберігає поточний стан: колір заливки, товщину лінії, трансформацію, область відсікання та шрифти. Всі операції малювання — лінії, прямокутники, текст, зображення — виконуються через CGContext. Згідно з Apple Developer Documentation, 2026, CGContext є фундаментом всієї растрової та векторної графіки в екосистемі Apple.

Головне

  • CGContext — графічний контекст, через який виконуються всі 2D-команди Core Graphics.
  • setStrokeColor та setFillColor керують кольором ліній та заливок у контексті.
  • addPath та drawPath дозволяють малювати довільні контури CGPath.
  • clip(to:) задає область відсікання для обмеження малювання певною зоною.
  • saveGState та restoreGState зберігають та відновлюють стек станів контексту.

Що таке CGContext?

CGContext — непрозорий тип (opaque type) з фреймворку Core Graphics, що представляє середовище для виконання команд 2D-рендерингу. Він керує стеком станів, поточним колірним простором, трансформацією координат та областю відсікання. CGContext є єдиним об'єктом, з яким взаємодіють всі функції малювання Quartz 2D — через нього проходять будь-які графічні операції.

CGContext існує в декількох різновидах залежно від призначення: bitmap context для роботи з растровими зображеннями в пам'яті, PDF context для генерації PDF-документів, window context для відмальовування на екрані (надається UIView в draw(_:)). Розробники iOS найчастіше працюють з контекстом, отриманим з UIGraphicsGetCurrentContext() всередині методу draw(_:) або з UIGraphicsBeginImageContextWithOptions для офлайн-рендерингу.

Контекст застосовує всі налаштування через стек станів — розробник може зберегти поточний стан (saveGState), змінити параметри та відновити попередній (restoreGState). Це критично важливо при відмальовуванні складних сцен, де різні елементи потребують різних кольорів, товщин ліній та трансформацій. Згідно з Apple WWDC 2023, правильне використання стеку станів підвищує читабельність коду та запобігає візуальним артефактам.

Керування станом: saveGState та restoreGState

CGContextSaveGState поміщає копію поточного стану контексту в стек. Цей стан включає всі налаштування: кольори, товщину лінії, трансформацію, тіні, область відсікання, шрифти. Після модифікації параметрів виклик CGContextRestoreGState повертає контекст до збереженого стану, скасовуючи всі проміжні зміни. Стек може містити до 32 станів на iOS.

Практичне застосування стеку

При малюванні груп елементів з різними стилями (наприклад, різнокольорові сектори діаграми) стек станів позбавляє необхідності запам'ятовувати та відновлювати кожен параметр вручну. Без saveGState/restoreGState розробнику довелося б перевстановлювати колір, тінь та трансформацію після кожного елемента. Використання стеку скорочує код та виключає помилки відновлення.

Важливо дотримуватися балансу збережень та відновлень — кожен saveGState повинен мати парний restoreGState. Порушення балансу призводить до витоку станів або передчасного відновлення, що викликає непередбачуване відображення. Статичний аналізатор Xcode не перевіряє баланс стеку CGContext, тому відповідальність за коректність лежить на розробнику. Рекомендується групувати пари в 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()
}

Кольори та заливки

CGContextSetFillColorWithColor встановлює колір заливки для наступних операцій. Колір передається як CGColorRef, який містить інформацію про колірний простір та компоненти кольору. CGContextSetStrokeColorWithColor працює аналогічно для кольору обведення. Apple рекомендує кешувати часто використовувані CGColorRef для підвищення продуктивності.

Градієнти в CGContext

CGContextDrawLinearGradient малює лінійний градієнт між двома точками із зазначеними кольорами. CGContextDrawRadialGradient створює радіальний градієнт, що переходить від одного кола до іншого. Для роботи з градієнтами спочатку створюється об'єкт CGGradientRef, що містить масив кольорів та позицій зупинок. Градієнти в CGContext підтримують як RGB, так і Grayscale колірні простори.

Заливка області градієнтом вимагає попереднього встановлення області відсікання. Без clip градієнт заповнить весь контекст, а не лише потрібну фігуру. Типовий патерн: додати шлях у контекст, викликати clip для обмеження області, потім намалювати градієнт. Цей патерн використовується в усіх застосунках, де потрібна градієнтна заливка складних форм, від кнопок до фонів.

ФункціяПризначенняКолірний простір
setFillColorВстановлює колір заливкиБудь-який (через CGColor)
setStrokeColorВстановлює колір обведенняБудь-який (через CGColor)
drawLinearGradientМалює лінійний градієнтRGB або Grayscale
drawRadialGradientМалює радіальний градієнтRGB або Grayscale

Малювання контурів та ліній

CGContextAddPath додає раніше створений CGPathRef у контекст для подальшого відображення. Після додавання шляху його можна обвести (strokePath), залити (fillPath) або виконати обидві операції. CGContextBeginPath починає новий шлях, очищаючи попередній. Контекст підтримує лише один активний шлях у кожен момент часу.

Прямі лінії та криві за точками

CGContextMoveToPoint переносить поточну точку контуру у вказані координати. CGContextAddLineToPoint малює пряму лінію від поточної точки до заданої. CGContextAddCurveToPoint додає кубічну криву Безьє, CGContextAddQuadCurveToPoint — квадратичну. CGContextClosePath замикає контур прямою лінією між останньою та першою точкою.

На відміну від UIBezierPath, де всі ці методи викликаються на об'єкті шляху, в CGContext вони викликаються безпосередньо на контексті. Ця відмінність важлива при виборі підходу: UIBezierPath зручніший для перевикористання контурів, CGContext — для одноразового малювання з мінімальними накладними витратами. CGContext також підтримує штрихові лінії через CGContextSetLineDash, гнучкі кінці через CGContextSetLineCap та з'єднання через 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()

Область відсікання та трансформації

CGContextClip обмежує область малювання поточним контуром. Всі наступні операції малювання будуть видимі лише всередині цієї області. Це базовий механізм для маскування, заокруглення кутів та створення нестандартних меж елементів. Область відсікання також зберігається та відновлюється через saveGState/restoreGState.

Трансформація координат

CGContextTranslateCTM зміщує початок координат контексту. CGContextScaleCTM масштабує осі X та Y. CGContextRotateCTM повертає систему координат навколо поточного початку. Трансформації накопичуються — кожна наступна застосовується відносно поточної системи координат. Складові трансформації (зміщення + поворот + масштаб) використовуються для анімації та позиціонування елементів.

Спільне використання відсікання та трансформації дає потужні можливості: можна намалювати складну маску через відсікання, а потім застосувати трансформацію до всього вмісту. Типовий приклад — відображення частини зображення під кутом із заокругленими краями. Порядок операцій важливий: спочатку встановити область відсікання, потім застосувати трансформацію, потім малювати вміст. Apple рекомендує мінімізувати кількість змін CTM (поточної матриці трансформації) в циклі малювання.

swift
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

Розглянемо повний приклад створення градієнтної діаграми з використанням CGContext. Діаграма включає три сектори різного кольору з градієнтною заливкою та текстовими підписами. Код демонструє комбінацію saveGState, clip, градієнтів та відмальовування тексту в одному контексті.

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

            // 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 або використання Metal для GPU-прискорення. Для більшості UI-задач з 5–10 елементами продуктивність CGContext залишається прийнятною.

Поширені запитання

Як отримати поточний CGContext в iOS?

Викличте UIGraphicsGetCurrentContext() всередині методу draw(_:) UIView або всередині блоку UIGraphicsBeginImageContextWithOptions. Для шарів CALayer використовуйте draw(in:) делегата CALayerDelegate, який отримує готовий контекст.

У чому різниця між fillPath та strokePath?

fillPath заливає внутрішню область поточного контуру кольором з setFillColor. strokePath малює лінію по контуру з параметрами setStrokeColor, setLineWidth та setLineCap. Обидві операції виконуються послідовно: спочатку заливка, потім обведення при необхідності.

Що таке bitmap CGContext?

Bitmap CGContext — контекст, створений через CGBitmapContextCreate, який малює в буфер пам'яті замість екрана. Використовується для фонової генерації зображень, обробки графіки без відображення та створення текстур для Metal або OpenGL.

Як скинути всі налаштування CGContext?

Core Graphics не надає методу скидання всіх налаштувань. Використовуйте saveGState перед змінами та restoreGState для повернення до початкового стану. Альтернативно можна створити новий контекст з тими ж параметрами.

Чому градієнт не відображається всередині фігури?

Перед викликом drawLinearGradient встановіть область відсікання через clip на потрібний контур. Без clip градієнт заповнює весь контекст. Порядок: addPath → clip → drawLinearGradient — гарантує градієнт всередині фігури.

Підсумки

  • CGContext — центральний об'єкт Core Graphics для всіх 2D-операцій малювання на iOS та macOS.
  • saveGState та restoreGState керують стеком станів контексту, запобігаючи побічним ефектам.
  • setFillColor та setStrokeColor задають кольори заливки та обведення через CGColorRef.
  • addPath, strokePath, fillPath працюють з довільними контурами CGPath.
  • clip обмежує область малювання поточним контуром для масок та заокруглень.
  • translateCTM, scaleCTM, rotateCTM трансформують систему координат контексту.
  • Всі операції CGContext виконуються на CPU — для складної графіки використовуйте CGLayer або Metal.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також