Presentation by Bob McCune of TapHarmonic detailing how to use the Quartz (CoreGraphics) framework with Swift 3.
Sample code available here: https://github.com/tapharmonic/QuartzDemosSwift
2. -Overview of the Quartz framework
- What is it?
- When do you need it?
-Core capabilities of framework:
- Contexts and Paths
- Drawing lines, curves, and images
- Applying transformations
-Recipes and Examples
Agenda
What will I learn?
4. -Apple’s 2D drawing framework based on the PDF imaging model
-Graphics API to produce lines, curves, transforms, gradients, etc.
-Uses painters model for drawing operations
What is Quartz 2D?
Wasn’t this supposed to be about Core Graphics?
Drawing Order Result
5. -Prefer to build your UI using images
- Better performance and caching
- Easier to create and maintain
- Keeps your graphics designers employed
-It’s best to design your UI using images…
When do I need it?
Isn’t this what graphics designers are for?
...until you can’t
15. -Graphics context provides the drawing area
-Manages core drawing states:
- Colors, line widths, transforms, etc.
- It’s a state machine
-Key Quartz Contexts:
- CGContext
- CGBitmapContext
- CGPDFContext
Quartz Graphics Contexts
The Canvas
16. -Current Transformation Matrix (CTM) defines the coordinate system
-Coordinate system varies on macOS and iOS/tvOS
Quartz Coordinate System
Drawing Orientation
0, 0
y
x
Positive 0, 0
y
x
Positive
macOS iOS and tvOS
17. -Drawing is defined in terms
of points
-Points are abstract,
infinitely thin
-Points live in User Space
CGContext Drawing
Quartz Points
p0
p1
19. -CGContext is the default drawing context
- Created by UIKit and AppKit
-Get a reference to it:
CGContext Drawing
CGContext
let context = UIGraphicsGetCurrentContext()
iOS and tvOS
let context = NSGraphicsContext.current()?.graphicsPort
macOS
23. Getting Started
Where do I draw?
class HelloQuartzViewController: UIViewController {
func drawBackground(recognizer: UIGestureRecognizer) {
}
}
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(randomColor())
context?.fill(view.bounds)
24. Getting Started
Where do I draw?
class HelloQuartzView: UIView {
override func draw(_ rect: CGRect) {
}
}
class HelloQuartzViewController: UIViewController {
func drawBackground(recognizer: UIGestureRecognizer) {
}
}
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(randomColor())
context?.fill(view.bounds)
25. Getting Started
Where do I draw?
class HelloQuartzView: UIView {
override func draw(_ rect: CGRect) {
}
}
class HelloQuartzViewController: UIViewController {
func drawBackground(recognizer: UIGestureRecognizer) {
}
}
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(randomColor())
context?.fill(view.bounds)
// Trigger call to HelloQuartzView's draw(:) method
helloView.setNeedsDisplay()
33. - CGColor is the native Quartz color type
- UIColor is the native UIKit color type
-Use UIColor in almost all cases
- Easier to create
- Named color initializers
- Easy to convert to and from CGColor
Defining Colors
CGColor vs UIColor
// Convert to Quartz Type
let cgColor = UIColor.red.cgColor
context.setFillColor(cgColor)
// Convert to UIKit Type
let uiColor = UIColor(cgColor: cgColor)
34. -Lines are the most essential primitive
-Lines are always defined by their endpoints
-Basic Recipe:
1. Begin a new path
2. Move to initial starting point
3. Add lines defining shape
4. Close the path (optional)
5. Draw Path
Drawing Lines
Defining Paths
43. -Arcs define circle segments
-Arcs are defined with the following:
- Center x, y coordinates
- Radius
- Start and stop angles (in radians)
- Direction (clockwise or counter-clockwise)
-Created using:
- addArc(center:radius:startAngle:endAngle:clockwise:)
- addArc(tangent1End:tangent2End:radius:)
Drawing Arcs
Constructing Paths
44. -Arc functions are not aware of flipped coordinate system
- Need to think upside down (or)
- Transform coordinate system before using
- UIBezierPath wraps Quartz GGPath functionality
- Access to underlying path with CGPath property
-Generally easier to use UIBezierPath on iOS and tvOS
- * In Swift 3, direct Quartz usages is very similar
UIBezierPath
More iOS/tvOS-friendly solution
53. -Quartz uses fill rules to determine what’s inside and outside of a
path.
-Uses one of the following:
- Non-zero Winding Rule (Default)
- Even Odd Rule
Fill Rules
To fill, or not to fill
55. -Affine transformations are essential to all graphics programming
regardless of platform
-Allow you to manipulate the coordinate system to translate,
scale, skew, and rotate
-Transform preserves collinearity of lines
Affine Transformations
Transforming the Coordinate System
X
56. -CTM can be modified with the following:
- translateBy(x:y:)
- scaleBy(x:y:)
- rotate(by:)
Current Transformation Matrix
Modifying the CTM
63. -A gradient is a fill that varies from one color to another
-Quartz supports two different gradient types
- Linear varies between two endpoints
- Radial varies between two radial endpoints
Gradients
Drawing Gradients
Linear Radial
64. -Gradients can be created using either:
- init(colorsSpace:colors:locations:)
- init(colorSpace:colorComponents:locations:)
-Both functions require the following:
- CGColorSpace
- Colors (components or array of CGColor)
- Array of CGFloat values defining gradient color stops
Creating a CGGradient
CGGradient
65. CGGradientRef
Building the gradient code
let startColor = UIColor.red
let endColor = UIColor.orange
let colors = [startColor.cgColor, endColor.cgColor] as CFArray
let locations: [CGFloat] = [0, 1]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient =
CGGradient(colorsSpace: colorSpace, colors: colors, locations: locations)!
let startPoint = CGPoint(x: rect.midX, y: rect.minY)
let endPoint = CGPoint(x: rect.midX, y: rect.maxY)
let opts = CGGradientDrawingOptions()
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: opts)
68. -Quartz has broad support for images
-Represented as CGImage
-Can be drawn using:
- draw(image:rect:)
- draw(image:rect:byTiling)
- CGBitmapContext allows for dynamically building image data
Working with Images
CGImage
69. -Don’t draw in Quartz. Use UIImageView.
-Choose UIImage over CGImage
- Easier to load and cache image content
- Provides methods for drawing
- Is aware flipped coordinate system
- UIImage provides a CGImage property
-Use CGImage for more advanced cases
- Cropping, scaling, masking
- Dynamic image creation
CGImage vs UIImage
Which should I use?
71. Using Image Masks
Hiding behind a mask
// Mask image created by toMask() extension
guard let maskImage = UIImage(named: “round_mask")?.cgImage?.toMask() else { return }
guard let jeffersonImage = UIImage(named: "jefferson")?.cgImage else { return }
if let masked = jeffersonImage.masking(maskImage) {
// Draw shadow to offset from background
let shadowOffset = CGSize(width: 1, height: 1)
let shadowColor = UIColor.darkGray.cgColor
context.setShadow(offset: shadowOffset, blur: 12, color: shadowColor)
let maskedImage = UIImage(cgImage: masked)
maskedImage.draw(in: drawingRect(for: maskedImage))
}
72. Cropping Images
cropping(to:)
let albumImage = UIImage(named: "vh1")!
let cropRect = CGRect(x: 20, y: 20, width: 200, height: 200)
if let cgImage = albumImage.cgImage?.cropping(to: cropRect) {
// Create new UIImage and use as needed
let eddieImage = UIImage(cgImage: cgImage)
}
73. -Quartz is a powerful drawing engine for Apple platforms
-Essential skill to master to build beautiful apps
-Quartz + Swift 3 = Awesome
Summary