1 of 51

Drawing with Core Graphics

MovingSmiles example

2 of 51

MovingSmiles

Core Graphics

            &

    Gesture Recognizers

3 of 51

Core Graphics

Top facts:

  • Painters model
  • Similar to Graphics2D in Java
  • Core Graphics means it's C not Objective-C
  • Subclass UIView override drawRect
  • Paint onto the context
    • Set graphics state (stroke color, fill color, etc)
    • Add geometry to the current path
    • Draw the path (stroke and/or fill)

4 of 51

Demo - MovingSmiles

5 of 51

MovingSmilesViewController

New File... UIViewController (without a .xib)

6 of 51

Universal app setup

App delegate headers

  • @class, ivar and property

App delegate implementation files

  • #import, synthesize, addSubview, and release

MainWindow xibs

  • Create MovingSmilesViewController connect to property

MovingSmiles

  • Make the view background blue in viewDidLoad
  • self.view.backgroundColor = [UIColor blueColor];

Run for iPhone and iPad, is the view blue?

7 of 51

Universal app so far

iPhone

iPad

8 of 51

Add touches

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

{

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

{

}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

{

}

9 of 51

Check for double clicks

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

    UITouch * anyTouch = [touches anyObject];

    if (anyTouch.tapCount == 2) {

        NSLog(@"Add a smile");

        // Create a Smile view 

    }

}

10 of 51

UIView subclass - SmileyFace

11 of 51

SmileyFace.m

- (id)initWithFrame:(CGRect)frame {

    if ((self = [super initWithFrame:frame])) {

        // Initialization code

        self.backgroundColor = [UIColor yellowColor];

    }

    return self;

}

12 of 51

Add a Smile on double click

// Create a Smile view 

CGFloat smileEdgeLength = (self.view.bounds.size.width + 

    self.view.bounds.size.height) / 8.0f;

SmileyFace * aSmile = [[SmileyFace alloc]

    initWithFrame:CGRectMake(0, 0, smileEdgeLength, 

    smileEdgeLength)];

aSmile.center = [anyTouch locationInView:self.view];

[self.view addSubview:aSmile];

[aSmile release];

13 of 51

Yellow views on double click

iPhone

iPad

14 of 51

Drawing

- (void)drawRect:(CGRect)rect {

    // Drawing code

    // Get the context

    CGContextRef context = UIGraphicsGetCurrentContext();  

    // Set graphics state parameters

    // Add geometry to the current path

    // Paint the context

    CGContextDrawPath(context, kCGPathFillStroke);  

}

15 of 51

CGContext functions

Setting Color, Color Space, and Shadow Values

  • CGContextSetFillColorWithColor
  • CGContextSetStrokeColorWithColor

Getting and Setting Graphics State Parameters

  • CGContextSetLineWidth

Constructing Paths - Geometry of the current path. For more CGPath Reference.

  • CGContextAddArc
  • CGContextAddCurveToPoint
  • CGContextAddLineToPoint
  • CGContextAddPath
  • CGContextAddQuadCurveToPoint
  • CGContextAddRect
  • CGContextBeginPath
  • CGContextClosePath
  • CGContextMoveToPoint
  • CGContextAddEllipseInRect

Painting Paths

  • CGContextDrawPath

16 of 51

Paint something

- (void)drawRect:(CGRect)rect {

    // Drawing code

    CGContextRef context = UIGraphicsGetCurrentContext();

    // Set graphics state parameters

    CGContextSetFillColorWithColor(context, 

        [UIColor redColor].CGColor);

    CGContextSetStrokeColorWithColor(context, 

        [UIColor blackColor].CGColor);

    // Add geometry to the current path

    CGContextAddEllipseInRect(context, 

                              CGRectMake(10, 10, 50, 50));

    

    // Paint the context    

    CGContextDrawPath(context, kCGPathFillStroke);  

}

17 of 51

CGContextDrawPath  

CGContextDrawPath(context, kCGPathFillStroke);  

void CGContextDrawPath (

   CGContextRef c,

   CGPathDrawingMode mode

);

enum CGPathDrawingMode {

   kCGPathFill,

   kCGPathEOFill,

   kCGPathStroke,

   kCGPathFillStroke,

   kCGPathEOFillStroke

};

18 of 51

Red dots

iPhone

iPad

19 of 51

Current Transformation Matrix

These functions allow you to examine and change the current transformation matrix (CTM) in a graphics context.

  • CGContextGetCTM
  • CGContextRotateCTM
  • CGContextScaleCTM
  • CGContextTranslateCTM
  •  (Other CGAffineTransform Make functions)
  • CGContextConcatCTM

20 of 51

Current Transformation Matrix

CGContextRef context = UIGraphicsGetCurrentContext(); 

CGAffineTransform ctm = CGContextGetCTM(context);

21 of 51

Set CTM to Identity

CGContextRef context = UIGraphicsGetCurrentContext();

CGAffineTransform ctm = CGContextGetCTM(context);

CGAffineTransform ctmInverse = CGAffineTransformInvert(ctm);

CGContextConcatCTM(context, ctmInverse);

Note: UITouch point locations would now need CGContextConvertPointToUserSpace (if needed)

22 of 51

CTM back to Identity

iPhone

iPad

23 of 51

Good habit - Push/Pop context

- (void)drawRect:(CGRect)rect {

    // Drawing code

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSaveGState(context);

    CGAffineTransform ctm = CGContextGetCTM(context);

    CGAffineTransform ctmInverse = CGAffineTransformInvert(ctm);

    CGContextConcatCTM(context, ctmInverse);

    ...

    CGContextRestoreGState(context);

}

Not actually useful here, but good practice

24 of 51

Start drawing a smile

- (void)drawRect:(CGRect)rect {

    ...

    // Set graphics state parameters

    CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);

    CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);

    // Add geometry to the current path

    CGContextAddEllipseInRect(context, self.bounds);

    ...

}

25 of 51

Remove the backgroundColor

- (id)initWithFrame:(CGRect)frame {

    if ((self = [super initWithFrame:frame])) {

        // Initialization code

        self.backgroundColor = [UIColor clearColor];

    }

    return self;

}

26 of 51

Line thickness

- (void)drawRect:(CGRect)rect {

    // Drawing code

    CGContextRef context = UIGraphicsGetCurrentContext();  // Get the context

    CGContextSaveGState(context);

    CGAffineTransform ctm = CGContextGetCTM(context);

    CGAffineTransform ctmInverse = CGAffineTransformInvert(ctm);

    CGContextConcatCTM(context, ctmInverse);

    

    // Set graphics state parameters

    CGContextSetLineWidth(context, 4.0f);

    CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);

    CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);

    // Add geometry to the current path

    CGContextAddEllipseInRect(context, CGRectInset(self.bounds, 2.0f, 2.0f));

    CGContextDrawPath(context, kCGPathFillStroke);  // Paint the context

    CGContextRestoreGState(context);

}

27 of 51

Yellow circles

iPhone

iPad

28 of 51

_eyeColor ivar

@interface SmileyFace : UIView {

    UIColor * _eyeColor;

}

29 of 51

eyeColor property

Header File

@interface SmileyFace : UIView {

UIColor * eyeColor;

}

Implementation File

@interface SmileyFace()

@property (nonatomic, retain) UIColor * eyeColor;

@end

@implementation SmileyFace

@synthesize eyeColor = _eyeColor;

...

- (void)dealloc {

    [_eyeColor release];

    [super dealloc];

}

30 of 51

Set the eyeColor

Add the randomColor category

- (id)initWithFrame:(CGRect)frame {

    if ((self = [super initWithFrame:frame])) {

        // Initialization code

        self.backgroundColor = [UIColor clearColor];

        self.eyeColor = [UIColor randomColor];

    }

    return self;

}

31 of 51

Drawing the eyes

- (void)drawRect:(CGRect)rect {

    ...

    CGContextAddEllipseInRect(context, CGRectInset(self.bounds, 2.0f, 2.0f));

    CGContextDrawPath(context, kCGPathFillStroke);  // Paint the context

    // Set graphics state parameters for the eye circles

    CGContextSetLineWidth(context, 2.0f);

    CGContextSetFillColorWithColor(context, self.eyeColor.CGColor);

    CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);

    // Add eye geometry

    CGContextDrawPath(context, kCGPathFillStroke);  // Paint the context

    CGContextRestoreGState(context);

}

32 of 51

Smile geometry

45 degree angle

0.25 eye radius

0.5 distance from center

Eyes

0.7 radius

-15 degree start angle

-165 degree end angle

Smile

33 of 51

Handy parameters

CGFloat smileRadius = self.bounds.size.width / 2.0f - 2.0f;

CGPoint centerPoint = CGPointMake(self.bounds.size.width/2.0f,

                            self.bounds.size.height/2.0f);

34 of 51

eye geometry

// Add eye geometry

CGFloat eyeRadius = smileRadius * 0.25;

CGFloat eyeXValue = centerPoint.x +

                     smileRadius*0.5*cos(45.0/180.0*M_PI);

eyeXValue -= eyeRadius;

CGFloat eyeYValue = centerPoint.y + 

                        smileRadius*0.5*sin(45.0/180.0*M_PI);

eyeYValue -= eyeRadius;

CGRect eyeRect = CGRectMake(eyeXValue, eyeYValue, 

                        eyeRadius*2.0f, eyeRadius*2.0f);

CGContextAddEllipseInRect(context, eyeRect);

35 of 51

Left eye is easy (two lines)

eyeRect.origin.x -= 2.0 * (smileRadius*0.5*cos(45.0/180.0*M_PI));

CGContextAddEllipseInRect(context, eyeRect);

36 of 51

Eyes

iPhone

iPad

37 of 51

Time for the mouth

// Set graphics state parameters for the mouth

// Add eye geometry

// Paint the context

CGContextDrawPath(context, kCGPathFillStroke);  

38 of 51

Geometry of the mouth

// Set graphics state parameters for the mouth

CGContextSetFillColorWithColor(context, 

                                [UIColor redColor].CGColor);

// Add eye geometry

CGContextBeginPath(context);

CGContextMoveToPoint(context, 

    centerPoint.x+0.7f*smileRadius*cos(-165.0/180.0*M_PI), 

    centerPoint.y+0.7f*smileRadius*sin(-165.0/180.0*M_PI));

CGContextAddLineToPoint(context, 

    centerPoint.x+0.7f*smileRadius*cos(-15.0/180.0*M_PI), 

    centerPoint.y+0.7f*smileRadius*sin(-15.0/180.0*M_PI));

CGContextAddArc(context, centerPoint.x, centerPoint.y,     

    0.7*smileRadius, -15.0f/180.0*M_PI, -165.0f/180.0*M_PI, 1);

CGContextClosePath(context);

CGContextDrawPath(context, kCGPathFillStroke);  

39 of 51

SmileyFaceWithSmile 

iPhone

iPad

40 of 51

Getting fancy with gradients

Drawing With a Gradient

  • CGContextDrawLinearGradient
  • CGContextDrawRadialGradient�

Drawing With a Shading - self investigation :)  

  • CGContextDrawShading

41 of 51

Drawing Gradients

CGContextDrawRadialGradient(CGContextRef context, 

    CGGradientRef gradient, 

    CGPoint startCenter, 

    CGFloat startRadius, 

    CGPoint endCenter, 

    CGFloat endRadius, 

    CGGradientDrawingOptions options);

CGContextDrawLinearGradient(CGContextRef context, 

    CGGradientRef gradient, 

    CGPoint startPoint, 

    CGPoint endPoint, 

    CGGradientDrawingOptions options);

42 of 51

Draw linear gradient

CGContextClip(context);

CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();

CGFloat gradientColorComponents[] = {0.7f, 0.7f, 0.7f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f};

CGFloat gradientLocations[] = {0.0,1.0};

   

CGGradientRef myGradient = CGGradientCreateWithColorComponents(rgbColorSpace, 

  gradientColorComponents, 

  gradientLocations, 

  2);

CGPoint gradientStartPoint = centerPoint;

CGPoint gradientEndPoint = centerPoint;

gradientStartPoint.y -= 0.1*smileRadius;

gradientEndPoint.y -= 0.7*smileRadius;

CGContextDrawLinearGradient(context, myGradient, 

                            gradientStartPoint, gradientEndPoint,

        kCGGradientDrawsAfterEndLocation | kCGGradientDrawsBeforeStartLocation);

CGColorSpaceRelease(rgbColorSpace);

CGGradientRelease(myGradient);

43 of 51

Saving to a CGPath

// Add mouth geometry

CGMutablePathRef mouthPath = CGPathCreateMutable();

CGPathMoveToPoint(mouthPath, &CGAffineTransformIdentity, 

  centerPoint.x+0.7f*smileRadius*cos(-165.0/180.0*M_PI), 

  centerPoint.y+0.7f*smileRadius*sin(-165.0/180.0*M_PI));

CGPathAddLineToPoint(mouthPath, &CGAffineTransformIdentity,

    centerPoint.x+0.7f*smileRadius*cos(-15.0/180.0*M_PI),

    centerPoint.y+0.7f*smileRadius*sin(-15.0/180.0*M_PI));

CGPathAddArc(mouthPath, &CGAffineTransformIdentity, 

centerPoint.x, centerPoint.y, 0.7*smileRadius, 

    -15.0f/180.0*M_PI, -165.0f/180.0*M_PI, 1);

CGPathCloseSubpath(mouthPath);

44 of 51

Clipping path for the gradient

CGContextSaveGState(context);

CGContextAddPath(context, mouthPath);

CGContextClip(context);

45 of 51

Adding an stroke outline on mouth

CGContextRestoreGState(context);

CGContextAddPath(context, mouthPath);

CGContextDrawPath(context, kCGPathStroke);

46 of 51

Fancy smiles

iPhone

iPad

47 of 51

Drawing images

UIImage * someImage = [UIImage imageNamed:@"r.png"];

[someImage drawInRect:[self bounds]];

[someImage drawAtPoint:self.center];

48 of 51

Drawing images

49 of 51

Drawing text

NSString * str = @"Hello, World!";

UIFont * systemFont = [UIFont systemFontWithSize:16.0f];

[str drawInRect:[self bounds] withFont: systemFont];

[str drawAtPoint:self.center withFont: systemFont];

50 of 51

Drawing Strings

51 of 51

Want more?

Free - Watch more Matt Stroker videos from University of Utah

Book:

  • Sprites
    • Using CTM in a general way
  • More Core Animation
  • OpenGL ES
  • Particle systems
  • Texture maps