Core Graphics is Apple's 2D rendering framework, part of Core Foundation, available on iOS, macOS, tvOS, and watchOS. It provides a low-level C API for working with vector and raster graphics, including drawing paths, gradients, shadows, images, and text. Core Graphics serves as the foundation for UIKit, SwiftUI, and AppKit. According to Apple Developer Documentation, 2026, the framework is used in more than 90% of iOS apps, because every visible element passes through Core Graphics or its hardware equivalent.
Key Takeaways
Core Graphics (also known as Quartz 2D) is a framework implementing Apple's 2D graphics engine. It is based on the Quartz portable graphics library and uses a page-based drawing model, where each action is a command in a sequence resembling drawing on paper. Core Graphics supports antialiasing, transparency (alpha compositing), and ICC color spaces.
The Core Graphics architecture is built around a graphics context (CGContext) that encapsulates the target output device — screen, printer, PDF document, or raster buffer. The context manages current settings: color, line width, transformation, clipping area, and fonts. This approach provides a uniform API for different output devices without changing the drawing code.
Core Graphics primarily runs on the CPU, using a vector model (resolution-independent). This means lines and curves are rasterized at the current device resolution when drawn. Unlike Metal, Core Graphics does not require explicit GPU resource management — the system handles all optimization. For most 2D tasks (graphics, charts, custom UI elements), Core Graphics performance is sufficient to maintain 60 FPS.
Core Graphics includes about 50 base types (opaque types), each responsible for specific functionality. All types follow Core Foundation memory management rules: they are created through functions with Create in the name, require a call to CGRelease, and are not ARC-compatible in Objective-C (however Swift automatically manages memory through CF_RETURNS_RETAINED annotations).
CGContext — the drawing context, the framework's central type. CGPath — an immutable vector path. CGColor — a color with a specified color space. CGGradient — a gradient fill. CGImage — a raster image. CGLayer — an optimized offline buffer for repeated drawing. CGDataProvider — a data source for images. CGColorSpace — a color space (sRGB, Display P3, Gray).
Each type is designed for minimal overhead: CGPath can be cached and reused across different contexts, CGColor is converted to GPU hardware representation when passed, CGLayer stores pre-rasterized content. This architecture allows Core Graphics to work efficiently both in simple UI tasks and in professional graphics editors.
| Type | Purpose | Memory Management |
|---|---|---|
| CGContext | Graphics context | CFRelease or automatic in Swift |
| CGPath | Vector path | CFRelease (immutable) |
| CGColor | Color in color space | CFRelease or CGColorCreate |
| CGImage | Raster image | CFRelease (after creation) |
| CGLayer | Offline buffer | CFRelease or CGLayerCreate |
CGPath is an immutable object describing a sequence of geometric segments: straight lines, Bezier curves, arcs, and closures. Unlike UIBezierPath, CGPath contains no drawing methods — it only stores geometry. To draw, CGPath is added to the context (CGContextAddPath) and then rendered via strokePath or fillPath. Immutability guarantees thread safety and cacheability.
CGPathCreateMutable creates a mutable path (CGMutablePathRef) for step-by-step construction. After final assembly, it is converted to an immutable CGPathRef. Apple recommends minimizing the number of points in CGPath — overly complex paths (more than 1000 segments) slow down rasterization. For optimization, use CGPathCreateCopyBySimplifyingPath, which removes redundant collinear points.
For shape animation between two CGPath objects, you can use CABasicAnimation on the path property of CAShapeLayer. Limitation: both CGPath objects must have the same number of segments (control points). The CGPathApply function lets you traverse all path elements and compare their structure. If segments do not match, the animation will be jerky — in this case, use CADisplayLink for manual point interpolation.
// create mutable path
let mutablePath = CGMutablePath()
mutablePath.moveTo(to: CGPoint(x: 0, y: 0))
mutablePath.addLine(to: CGPoint(x: 100, y: 0))
mutablePath.addLine(to: CGPoint(x: 100, y: 100))
mutablePath.closeSubpath()
// convert to immutable for safe caching
let immutablePath: CGPath = mutablePath
CATransaction.perform {
shapeLayer.path = immutablePath
}
CGGradient is an object defining a smooth transition between two or more colors along a straight line (linear gradient) or from the center outward (radial gradient). CGGradient is created with a specified color space, an array of CGColorRef objects, and an array of stop positions from 0.0 to 1.0. After creation, the gradient can be reused multiple times in different contexts.
CGShading is a more powerful mechanism that allows defining a color function along a path. Unlike CGGradient, which works with a fixed set of colors, CGShading uses a callback function (CGFunctionRef) that computes the color at each point. This enables complex gradients with non-linear transitions and composite color schemes.
To draw a gradient inside a shape, you need a clip on the shape's path. The order: add the path to the context, call CGContextClip, then draw the gradient. Without a clip, the gradient fills the entire context. On iOS, CGGradient is recommended as the simpler and more performant option — CGShading is only justified for custom color functions.
func drawGradient(ctx: CGContext, rect: CGRect) {
let colors = [UIColor.red.cgColor, UIColor.blue.cgColor] as CFArray
guard let gradient = CGGradient(
colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: colors,
locations: [0.0, 1.0]
) else { return }
ctx.saveGState()
ctx.addEllipse(in: rect)
ctx.clip()
ctx.drawLinearGradient(
gradient,
start: CGPoint(x: rect.midX, y: rect.minY),
end: CGPoint(x: rect.midX, y: rect.maxY),
options: []
)
ctx.restoreGState()
}
CGImage is a raster image in Core Graphics that can be loaded from a file, created programmatically, or obtained from UIImage. CGImage contains pixel data, color space information, size, bit depth, and data layout. Unlike UIImage, CGImage does not manage Retina scaling — this responsibility falls on the developer via the scale parameter.
CGLayer is an offline buffer for pre-rasterizing static content. If the same set of drawing commands is executed repeatedly (for example, a background pattern), CGLayer rasterizes it once and then simply copies it into the context. According to Apple, using CGLayer can speed up repeated drawing by 2–5× on CPU and up to 10× with hardware acceleration.
For image loading, use CGImageSource — a powerful API for reading metadata, thumbnails, and individual frames from GIF/APNG. CGImageSource allows progressive loading and retrieving only the necessary fragments. When working with large images (exceeding the screen size), it is recommended to create a CGImageSource with disk caching and use CGImageSourceCreateThumbnailAtIndex to display a reduced version.
func createLayer(ctx: CGContext, size: CGSize) -> CGLayer? {
guard let layer = CGLayer(ctx, size: size, auxiliaryInfo: nil) else { return nil }
let layerCtx = layer.context !
// draw once
layerCtx.setFillColor(UIColor.lightGray.cgColor)
layerCtx.addEllipse(in: CGRect(origin: .zero, size: size))
layerCtx.fillPath()
return layer
}
// usage in draw()
for i in 0..<20 {
ctx.draw(layer, at: CGPoint(x: CGFloat(i) * 30, y: 0))
}
Let's build a custom View for displaying a watermark on an image using Core Graphics. The watermark includes semi-transparent text and a vector logo overlaid on top of the original image. The code demonstrates working with CGContext, CGImage, text, and transparency in a single context.
class WatermarkView: UIView {
private let watermarkText = "IT Sectr"
private let watermarkOpacity: CGFloat = 0.3
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
// draw background image
UIImage(named: "background")?.draw(in: rect)
// configure watermark transparency
ctx.saveGState()
ctx.translateBy(x: rect.midX, y: rect.midY)
ctx.rotate(by: -.pi / 6)
// semi-transparent text
let attributes: NSAttributedString.Attributes = [
.font: UIFont.boldSystemFont(ofSize: 48),
.foregroundColor: UIColor.white.withAlphaComponent(watermarkOpacity)
]
let textSize = watermarkText.size(withAttributes: attributes)
watermarkText.draw(
at: CGPoint(x: -textSize.width / 2, y: -textSize.height / 2),
withAttributes: attributes
)
// vector icon (circle with cross) below text
ctx.setStrokeColor(UIColor.white.withAlphaComponent(watermarkOpacity).cgColor)
ctx.setLineWidth(3)
ctx.addEllipse(in: CGRect(x: -25, y: -75, width: 50, height: 50))
ctx.strokePath()
ctx.moveTo(x: -15, y: -50)
ctx.addLineTo(x: 15, y: -50)
ctx.moveTo(x: 0, y: -65)
ctx.addLineTo(x: 0, y: -35)
ctx.strokePath()
ctx.restoreGState()
}
}
The code uses key Core Graphics features: saveGState/restoreGState to isolate watermark effects from the background, translateBy and rotate for positioning and rotation, semi-transparent text via withAlphaComponent, and a vector icon through addEllipse and addLineTo. The entire watermark is drawn as a single composition after the background image.
When creating watermarks for photos in production, it is important to consider performance. If the image is larger than the screen, reduce it using CGImageSourceCreateThumbnailAtIndex before applying the watermark. Core Graphics handles all operations on the CPU — for batch processing of dozens of images, use a background queue and CGBitmapContext to avoid blocking the UI.
Frequently Asked Questions
UIKit is a high-level UI framework that uses Core Graphics internally for drawing. Core Graphics is a low-level C API for 2D graphics. UIKit is more convenient for standard elements, while Core Graphics gives full control over every pixel.
Core Graphics is suitable for static 2D graphics, charts, and custom UI elements. Metal is for 3D graphics, complex animations, and processing large pixel arrays with GPU acceleration. For 95% of UI tasks, Core Graphics is sufficient.
Use CGImageSource to load thumbnails via createThumbnailAtIndex instead of full loading. For displaying large images, create CGImage with the downsample option. Release CGImage via CGImageRelease when it is no longer needed.
Yes, Core Graphics includes full PDF support through CGPDFDocument for reading and CGPDFContext for creating PDF documents. You can draw any CGContext content directly into a PDF page while preserving vectors.
Core Graphics automatically uses the device resolution when drawing on screen. For offline contexts, use UIGraphicsBeginImageContextWithOptions with scale = 0 (system scale) or explicitly specify 2.0/3.0 for Retina/Retina HD.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also