Drawing with Quartz on iOS

74
http://bobmccune.com http://tapharmonic.com Drawing with Quartz on iOS

description

Presentation by Bob McCune of TapHarmonic detailing how to use the Quartz framework to perform 2D drawing on the iOS platform. Quartz Demos: https://github.com/tapharmonic/QuartzDemos

Transcript of Drawing with Quartz on iOS

Page 2: Drawing with Quartz on iOS

Agenda

• Overview of the Quartz framework• What is it?

• When do you need it?

• Key framework functionality:• Contexts and Paths

• Drawing lines, curves, and images

• Applying transformations

• Recipes and Examples

Page 3: Drawing with Quartz on iOS

What is Quartz?

Page 4: Drawing with Quartz on iOS

Wasn’t this talk supposed to be about Core Graphics?

What is Quartz?

• 2D drawing engine for Mac and iOS based on the PDF imaging model

• Graphics API to produce lines, curves, transformations, gradients, etc.

• Uses painters model for drawing operations

Drawing Order Result

Page 5: Drawing with Quartz on iOS

Isn’t this what graphic designers are for?

When do I need it?

• Always build your UI from images• Better performance and caching

• Easier to create and maintain

• Keeps your graphic designers employed

• Always, always, always use images...

...until you can’t

Page 6: Drawing with Quartz on iOS
Page 7: Drawing with Quartz on iOS
Page 8: Drawing with Quartz on iOS
Page 9: Drawing with Quartz on iOS
Page 10: Drawing with Quartz on iOS

Getting Started

Page 11: Drawing with Quartz on iOS

Dude, where’s my objects?

Understanding the Syntax

Objective-CCGContext *context = [[CGContext alloc] init];[context moveToPoint:CGPointMake(12.0f, 12.0f)];[context addLineToPoint:CGPointMake(24.0f, 24.0f)];[context fill];

Page 12: Drawing with Quartz on iOS

Dude, where’s my objects?

Understanding the Syntax

Objective-CCGContext *context = [[CGContext alloc] init];[context moveToPoint:CGPointMake(12.0f, 12.0f)];[context addLineToPoint:CGPointMake(24.0f, 24.0f)];[context fill];

Standard CCGContextRef context = UIGraphicsGetCurrentContext();CGContextMoveToPoint(context, 12.0f, 12.0f);CGContextAddLineToPoint(context, 24.0f, 24.0f);CGContextFillPath(context);

Quartz is not an Objective-C API

Page 13: Drawing with Quartz on iOS

Naming Conventions

Quartz is Consistent

CGContextMoveToPoint(context, 12.0f, 12.0f);

<Type><Action>(<Object>, <Arguments);

CGPDFContextClose(context);

CGPathAddLineToPoint(path, NULL, 22.0f, 100.0f);

Functions always use the following convention:

Page 14: Drawing with Quartz on iOS

Graphics Contexts

Page 15: Drawing with Quartz on iOS

The Canvas

Quartz Graphics Contexts

• 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

Page 16: Drawing with Quartz on iOS

Drawing Orientation

Quartz Coordinate System

• Current Transformation Matrix (CTM) defines the coordinate system

• Coordinate system varies on Mac and iOS

0, 0

y

x

Positive 0, 0

y

x

PositiveMac OS X iOS

Page 17: Drawing with Quartz on iOS

Quartz Points

CGContext Drawing

• Drawing is defined in terms of points

• Points are abstract, infinitely thin

• Points live in User Space

p0

p1

Page 18: Drawing with Quartz on iOS

Think in points not pixels

Points are Device-Independent

p0

p1

Page 19: Drawing with Quartz on iOS

CGContextRef

CGContext Drawing

• CGContextRef is the drawing context• Created by UIKit and AppKit

• Get a pointer reference to it:

CGContextRef context = UIGraphicsGetCurrentContext();iOS

NSGraphicsContext *current = [NSGraphicsContext currentContext];CGContextRef context = (CGContextRef)[current graphicsPort];

Mac

Page 20: Drawing with Quartz on iOS

Where do I draw?

Getting Started

Page 21: Drawing with Quartz on iOS

Where do I draw?

Getting Started

@implementation HelloQuartzViewController

- (void)drawBackground:(UIGestureRecognizer *)recognizer { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, THRandomColor()); CGContextFillRect(context, self.view.bounds);}

@end

Page 22: Drawing with Quartz on iOS

Where do I draw?

Getting Started

@implementation HelloQuartzViewController

- (void)drawBackground:(UIGestureRecognizer *)recognizer {

}

@end

CGContextRef context = UIGraphicsGetCurrentContext();CGContextSetFillColorWithColor(context, THRandomColor());CGContextFillRect(context, self.view.bounds);

@implementation HelloQuartzView

- (void)drawRect:(CGRect)rect {

}

@end

Page 23: Drawing with Quartz on iOS

Where do I draw?

Getting Started

@implementation HelloQuartzViewController

- (void)drawBackground:(UIGestureRecognizer *)recognizer {

// Trigger call to HelloQuartzView's drawRect method (if needed) [self.helloView setNeedsDisplay];}

@end

CGContextRef context = UIGraphicsGetCurrentContext();CGContextSetFillColorWithColor(context, THRandomColor());CGContextFillRect(context, rect);

@implementation HelloQuartzView

- (void)drawRect:(CGRect)rect {

}

@end

Page 24: Drawing with Quartz on iOS

Basic Paths

Page 25: Drawing with Quartz on iOS

Defining PathsCGContext Drawing

(0, 0)

(100, 100)

CGRect rect = CGRectMake(0, 0, 100, 100);CGContextAddRect(context, rect);

Page 26: Drawing with Quartz on iOS

Defining PathsCGContext Drawing

CGRect rect = CGRectMake(0, 0, 100, 100);CGContextAddEllipseInRect(context, rect);

(0, 0)

(100, 100)

Page 27: Drawing with Quartz on iOS

Defining PathsCGContext Drawing

CGContextSetStrokeColorWithColor(context, white);CGContextDrawPath(context, kCGPathStroke);

(0, 0)

(100, 100)

Page 28: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGContextSetFillColorWithColor(context, yellow);CGContextDrawPath(context, kCGPathFill);

(0, 0)

(100, 100)

Page 29: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGContextDrawPath(context, kCGPathFillStroke);

(0, 0)

(100, 100)

Page 30: Drawing with Quartz on iOS

CGColorRef

Defining Colors

• Colors must be defined before drawing

• Fill Colors• CGContextSetFillColor• CGContextSetFillColorWithColor• CGContextSetRGBFillColor

• Stroke Colors• CGContextSetStrokeColor• CGContextSetStrokeColorWithColor• CGContextSetRGBStrokeColor

Page 31: Drawing with Quartz on iOS

CGColor vs UIColor

Defining Colors

• CGColor is the native Quartz color type

• UIColor is the native UIKit color type

• Use UIColor in almost all cases• Easier to create

• Autoreleased convenience initializers

• Named color initializers

• Easy to convert to and from CGColor

CGColorRef redColor = [UIColor redColor].CGColor;CGContextSetFillColorWithColor(context, redColor);

Page 32: Drawing with Quartz on iOS

Defining Paths

Drawing Lines

• 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.Optionally close the path5.Draw Path

Page 33: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGContextBeginPath(context);CGContextMoveToPoint(context, 0.0f, 0.0f);

Page 34: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGContextAddLineToPoint(context, 0.0f, 4.0f);

Page 35: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGContextAddLineToPoint(context, 4.0f, 4.0f);

Page 36: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGContextAddLineToPoint(context, 4.0f, 0.0f);

Page 37: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGContextClosePath(context);

Page 38: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGColorRef color = [UIColor whiteColor].CGColor;CGContextSetFillColorWithColor(context, color);

Page 39: Drawing with Quartz on iOS

Defining Paths

CGContext Drawing

CGContextDrawPath(context, kCGPathFill);

Page 40: Drawing with Quartz on iOS

Advanced Paths

Page 41: Drawing with Quartz on iOS

Defining Paths

Drawing Arcs

• Arcs define circle segments

• Arcs are defined with the following:• Center point x, y coordinates

• Radius

• Start and stop angles (in radians)

• Direction (clockwise or counter-clockwise)

• Created using:• CGContextAddArc• CGContextAddArcToPoint

Page 42: Drawing with Quartz on iOS

More iOS-friendly solution

UIBezierPath

• Arc functions are not aware of flipped coordinate system• Need to think upside down

• Transform coordinate system before using

• UIBezierPath provides a lightweight wrapper over Quartz path functionality

• Access to underlying path with CGPath property

• Easier to use UIBezierPath on iOS

Page 43: Drawing with Quartz on iOS

Defining Paths

Drawing Arcs

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:centerPoint radius:radius startAngle:0 endAngle:DEGREES(90) clockwise:NO];CGContextAddPath(context, path.CGPath);

Page 44: Drawing with Quartz on iOS

Single Control Point

Quadratic Bézier Curves

p0

CGContextBeginPath(context);CGContextMoveToPoint(context, startPoint.x, startPoint.y);

Page 45: Drawing with Quartz on iOS

Single Control Point

Quadratic Bézier Curves

p0

p1

cp

CGContextAddQuadCurveToPoint(context, cp.x, cp.y, endPoint.x, endPoint.y);

Page 46: Drawing with Quartz on iOS

Single Control Point

Quadratic Bézier Curves

CGColorRef greenColor = [UIColor greenColor].CGColor;CGContextSetStrokeColorWithColor(context, greenColor);CGContextStrokePath(context);

p0

p1

cp

Page 47: Drawing with Quartz on iOS

Two Control Points

Cubic Bézier Curves

p0

CGContextBeginPath(context);CGContextMoveToPoint(context, startPoint.x, startPoint.y);

Page 48: Drawing with Quartz on iOS

Two Control Points

Cubic Bézier Curves

p1CGContextAddCurveToPoint(context, cp1.x, cp1.y, cp2.x, cp2.y, endPoint.x, endPoint.y);

p0

cp1

cp2

Page 49: Drawing with Quartz on iOS

Two Control Points

Cubic Bézier Curves

CGContextAddCurveToPoint(context, -cp2.x, cp2.y, -cp1.x, cp1.y, endPoint.x, endPoint.y);

p1

p0

cp1

cp2

-cp1

-cp2

Page 50: Drawing with Quartz on iOS

Two Control Points

Cubic Bézier Curves

p0

p1CGColorRef fillColor = [UIColor redColor].CGColor;CGContextSetFillColorWithColor(context, fillColor);CGContextClosePath(context);CGContextDrawPath(context, kCGPathFillStroke);

cp1

cp2

-cp1

-cp2

Page 51: Drawing with Quartz on iOS

To fill, or not to fill

Fill Rules

• 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

Page 52: Drawing with Quartz on iOS

CGPathRef and UIBezierPath

Reusable Paths

• Paths are flushed from context when drawn• CGPath or UIBezierPath should be used when

you need a reusable path

CGMutablePathRef path = CGPathCreateMutable();CGPathMoveToPoint(path, NULL, 0, 55);CGPathAddQuadCurveToPoint(path, NULL, -50, 155, 0, 155);CGPathAddQuadCurveToPoint(path, NULL, 50, 155, 0, 55);

CGContextAddPath(context, path);CGContextDrawPath(context, kCGPathFillStroke);

CGPathRelease(path);

Page 53: Drawing with Quartz on iOS

Transformations

Page 54: Drawing with Quartz on iOS

Transforming the Coordinate System

Affine Transformations

• 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

X

Page 55: Drawing with Quartz on iOS

Modifying the CTM

Current Transformation Matrix

• CTM can be modified with the following:• CGContextTranslateCTM• CGContextScaleCTM• CGContextRotateCTM

• Additionally can create a CGAffineTransform

Page 56: Drawing with Quartz on iOS

Moving the Origin Point

CGContextTranslateCTM

(0, 0)

(100, 100)

Page 57: Drawing with Quartz on iOS

Moving the Origin Point

CGContextTranslateCTM

(0, 0)(0, 0)

CGContextTranslateCTM(context, 100.0f, 100.0f);

Page 58: Drawing with Quartz on iOS

Scaling the Coordinates

CGContextScaleCTM

(0, 0)

(200, 200)

Page 59: Drawing with Quartz on iOS

Scaling the Unit Size

CGContextScaleCTM

(0, 0)

(200, 200)

CGContextScaleCTM(context, 2.0f, 2.0f);

Page 60: Drawing with Quartz on iOS

CGContextRotateCTM

Page 61: Drawing with Quartz on iOS

CGContextRotateCTM

CGContextRotateCTM(context, DEGREES(15));

Page 62: Drawing with Quartz on iOS

Gradients

Page 63: Drawing with Quartz on iOS

Drawing Gradients

Gradients

• 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

Linear Radial

Page 64: Drawing with Quartz on iOS

CGGradientRef

Creating a CGGradient

• Gradients can be created using either:• CGGradientCreateWithColors• CGGradientCreateWithColorComponents

• Both functions require the following:• CGColorSpace• Colors (components or array of CGColorRef)

• CGFloat[] defining gradient color stops

Page 65: Drawing with Quartz on iOS

Building the gradient code

CGGradientRef

// Define ColorsCGColorRef startColor = [UIColor greenColor].CGColor;CGColorRef endColor = [UIColor yellowColor].CGColor;NSMutableArray *colors = [NSMutableArray array];[colors addObject:(id)startColor];[colors addObject:(id)endColor];// Define Color LocationsCGFloat locations[] = {0.0, 1.0};// Create GradientCGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)colors, locations);// Define start and end points and draw gradientCGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);

Page 66: Drawing with Quartz on iOS

Gradient Examples

Page 67: Drawing with Quartz on iOS

Images

Page 68: Drawing with Quartz on iOS

CGImageRef

Working with Images

• Quartz has broad support for images

• Represented as CGImageRef

• Can be drawn using:• CGContextDrawImage• CGContextDrawTiledImage

• CGBitmapContext allows for dynamically building image data

Page 69: Drawing with Quartz on iOS

Which should I use?

CGImage vs UIImage

• Don’t draw in Quartz. Use UIImageView.

• Choose UIImage over CGImage• Easier to load and cache image content

• Provides methods for drawing

• Is aware of flipped coordinate system

• Easy to convert to and from CGImage

• Use CGImage for more advanced cases• Cropping, scaling, masking

• Dynamic image creation

Page 70: Drawing with Quartz on iOS

Hiding behind a mask

Using Image Masks

Page 71: Drawing with Quartz on iOS

Hiding behind a mask

Using Image Masks

UIImage *maskImage = [UIImage imageNamed:@"round_mask"]; UIImage *jeffersonImage = [UIImage imageNamed:@"jefferson"]; CGImageRef maskRef = maskImage.CGImage; CGImageRef imageMask = CGImageMaskCreate(CGImageGetWidth(maskRef), CGImageGetHeight(maskRef), CGImageGetBitsPerComponent(maskRef), CGImageGetBitsPerPixel(maskRef), CGImageGetBytesPerRow(maskRef), CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef masked = CGImageCreateWithMask(jeffersonImage, imageMask); UIImage *maskedImage = [UIImage imageWithCGImage:masked];

Page 72: Drawing with Quartz on iOS

CGImageCreateWithImageInRect

Cropping Images

UIImage *albumImage = [UIImage imageNamed:@"vh1"];CGRect cropRect = CGRectMake(20.0f, 20.0f, 200.0f, 200.0f);CGImageRef image = CGImageCreateWithImageInRect(albumImage.CGImage, cropRect);CGRect imageRect = CGRectMake(0.0f, 0.0f, 200.0f, 200.0f);// Draw image in current context[[UIImage imageWithCGImage:image] drawInRect:imageRect];CGImageRelease(image);

Page 73: Drawing with Quartz on iOS

Summary• Quartz is a powerful, comprehensive

drawing engine and API for Apple platforms

• Fairly steep learning curve, but consistent interface helps considerably

• Understanding is essential to building beautiful apps on Mac and iOS

Page 74: Drawing with Quartz on iOS

Resources

Quartz 2D Programming Guidehttp://developer.apple.com/

Programming with QuartzDavid GelphmanBunny Laden

Quartz Demoshttps://github.com/tapharmonic/QuartzDemos