Episode #285
Clipping Paths
Series: Dive Into Core Graphics

Episode #285
// create path
let path = CGMutablePath()
path.addEllipse(in: CGRect(x: 25, y: 25, width: 100, height: 100))
// clip to the path
context.addPath(path)
context.clip()
// draw inside the clipped area
context.setFillColor(CGColor(colorSpace: colorSpace, components: [1, 0, 0, 1])!)
context.fill(CGRect(x: 50, y: 50, width: 100, height: 100))
Notice that the rect we filled will only be drawn inside the circle path we defined earlier.
It isn't really possible to undo a clipping operation (other than resetting the clip to be the entire drawing rect), so if you want to add more drawing outside a previously clipped path, you'll have to rely on the graphics state stack:
// save the current graphics state
context.saveGState()
// create path
let path = CGMutablePath()
path.addEllipse(in: CGRect(x: 25, y: 25, width: 100, height: 100))
// clip to the path
context.addPath(path)
context.clip()
// draw inside the clipped area
context.setFillColor(CGColor(colorSpace: colorSpace, components: [1, 0, 0, 1])!)
context.fill(CGRect(x: 50, y: 50, width: 100, height: 100))
// 'pop' the state off the stack, undoing any previous drawing operations
context.restoreGState()
// draw more stuff, which won't be clipped
context.setFillColor(CGColor(colorSpace: colorSpace, components: [0, 0, 1, 1])!)
context.fill(CGRect(x: 150, y: 150, width: 100, height: 100))
We can do more complex drawings as well and clip them to get interesting effects. This might be used as the basis for an iOS-style icon:
let path = CGMutablePath()
path.addRoundedRect(in: CGRect(x: 50, y: 50, width: 100, height: 100), cornerWidth: 10, cornerHeight: 10)
context.addPath(path)
context.clip()
let colors = [
CGColor(colorSpace: colorSpace, components: [1, 0, 0, 1])!,
CGColor(colorSpace: colorSpace, components: [1, 0, 1, 1])!
] as CFArray
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: nil)!
context.drawLinearGradient(gradient,
start: CGPoint(x: 50, y: 50),
end: CGPoint(x: 50, y: 150),
options: [.drawsAfterEndLocation, .drawsBeforeStartLocation])
This episode uses Swift 3.0, Xcode 8.3.