CGContext — основной объект Core Graphics для выполнения 2D-команд рисования в iOS и macOS. Он представляет собой графический контекст, который хранит текущее состояние: цвет заливки, толщину линии, трансформацию, область отсечения и шрифты. Все операции рисования — линии, прямоугольники, текст, изображения — выполняются через CGContext. По данным Apple Developer Documentation, 2026, CGContext является фундаментом всей растровой и векторной графики в экосистеме Apple.
Главное
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, правильное использование стека состояний повышает читаемость кода и предотвращает визуальные артефакты.
CGContextSaveGState помещает копию текущего состояния контекста в стек. Это состояние включает все настройки: цвета, толщину линии, трансформацию, тени, область отсечения, шрифты. После модификации параметров вызов CGContextRestoreGState возвращает контекст к сохранённому состоянию, отменяя все промежуточные изменения. Стек может содержать до 32 состояний на iOS.
При рисовании группы элементов с разными стилями (например, разноцветные сектора диаграммы) стек состояний избавляет от необходимости запоминать и восстанавливать каждый параметр вручную. Без 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 рисует линейный градиент между двумя точками с указанными цветами. 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.
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 (Current Transformation Matrix) в цикле отрисовки.
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. Диаграмма включает три сектора разного цвета с градиентной заливкой и текстовыми подписями. Код демонстрирует комбинацию 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 или использования Metal для GPU-ускорения. Для большинства UI-задач с 5–10 элементами производительность CGContext остаётся приемлемой.
Часто задаваемые вопросы
Вызовите UIGraphicsGetCurrentContext() внутри метода draw(_:) UIView или внутри UIGraphicsBeginImageContextWithOptions блока. Для слоёв CALayer используйте draw(in:) делегата CALayerDelegate, который получает готовый контекст.
fillPath заливает внутреннюю область текущего контура цветом из setFillColor. strokePath рисует линию по контуру с параметрами setStrokeColor, setLineWidth и setLineCap. Обе операции выполняются последовательно: сначала fill, затем stroke при необходимости.
Bitmap CGContext — контекст, созданный через CGBitmapContextCreate, который рисует в буфер памяти вместо экрана. Используется для фоновой генерации изображений, обработки графики без отображения и создания текстур для Metal или OpenGL.
Core Graphics не предоставляет метода сброса всех настроек. Используйте saveGState перед изменениями и restoreGState для возврата к исходному состоянию. Альтернативно можно создать новый контекст с теми же параметрами.
Перед вызовом drawLinearGradient установите область отсечения через clip на нужный контур. Без clip градиент заполняет весь контекст. Порядок: addPath → clip → drawLinearGradient — гарантирует градиент внутри фигуры.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также