Custom Drawing & Animation CS 442: Mobile App Development - - PowerPoint PPT Presentation

custom drawing animation
SMART_READER_LITE
LIVE PREVIEW

Custom Drawing & Animation CS 442: Mobile App Development - - PowerPoint PPT Presentation

Custom Drawing & Animation CS 442: Mobile App Development Michael Saelee <lee@iit.edu> Frameworks - UIKit - Core Graphics / Quartz - Core Animation - OpenGL ES UIKit OpenGL ES Core Core Graphics Animation UIKit (mostly)


slide-1
SLIDE 1

Custom Drawing & Animation

CS 442: Mobile App Development Michael Saelee <lee@iit.edu>

slide-2
SLIDE 2

Frameworks

  • UIKit
  • Core Graphics / Quartz
  • Core Animation
  • OpenGL ES
slide-3
SLIDE 3

UIKit Core Graphics OpenGL ES Core Animation

slide-4
SLIDE 4

UIKit (mostly) Swift/ObjC API UI... classes/functions

slide-5
SLIDE 5

Base view class: UIView Pre-built controls: UIControls UIKit

slide-6
SLIDE 6

typically, use concrete subclasses as is (no need to subclass) e.g., UILabel, UIButton, UITableView, UIImageView UIKit

slide-7
SLIDE 7

can also subclass UIView to draw custom 
 UI elements UIKit

slide-8
SLIDE 8

support for

  • 2D drawing
  • transformations
  • predefined transitions
  • basic animation

UIKit

slide-9
SLIDE 9

CG aka “Quartz 2D” C API for drawing

slide-10
SLIDE 10

support for:

  • layer-based graphics
  • patterns, gradients, etc.
  • color spaces
  • working with bitmaps

CG

slide-11
SLIDE 11

mostly, UIKit draws using CG i.e., more than one way of doing anything CG

slide-12
SLIDE 12

CG UIKit

// get current graphics context to draw into CGContextRef context = UIGraphicsGetCurrentContext(); // clear with white rectangle CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); CGContextFillRect(context, self.bounds); // load image from file CGDataProviderRef provider = CGDataProviderCreateWithFilename(imageFileName); CGImageRef image = CGImageCreateWithPNGDataProvider(provider, NULL, true, kCGRenderingIntentDefault); CGDataProviderRelease(provider); // draw image at (0,0) CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); CGImageRelease(image); // clear with white rectangle UIColor.whiteColor().set() UIRectFill(CGRect(x: 0, y: 0, width: 100, height: 100)) // load and draw image at (0,0) UIImage(named: “image.png")?.drawAtPoint(CGPoint(x: 0, y: 0))
slide-13
SLIDE 13

CA API for animation and compositing

verb [ trans. ] [usu. as n. ] ( compositing) combine (two or more images) to make a single picture,

  • esp. electronically : photographic compositing by computer.
slide-14
SLIDE 14

all UIViews are backed by CA layers CA

slide-15
SLIDE 15

can create a hierarchy of CALayers 
 within a single view (in addition to creating a hierarchy of views) CA

slide-16
SLIDE 16

generally, layers are:

  • more lightweight
  • more flexible
  • more complex

CA

slide-17
SLIDE 17

CALayer properties can be 
 automatically animated CA

slide-18
SLIDE 18

support for:

  • simple value interpolation
  • key frame animation
  • transition effects
  • animation groups

CA

slide-19
SLIDE 19

CA

// create new CA layer and populate with image CALayer *layer = [CALayer layer]; UIImage *image = [UIImage imageNamed:@"image.png"]; layer.contents = image.CGImage; layer.frame = CGRectMake(0, 0, image.size.width, image.size.height); // add layer to view layer [self.view.layer addSublayer:layer]; // create basic animation to interpolate position between (100,100) and (300,300) CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"]; anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)]; anim.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 300)]; anim.duration = 5.0; anim.autoreverses = YES; anim.repeatCount = HUGE_VALF; anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; [layer addAnimation:anim forKey:@"backandforth"]; // load image and add to view at position (100,100) let imageView = UIImageView(image: UIImage(named: "image.png")) imageView.center = CGPoint(x: 100, y: 100) view.addSubview(imageView) // animate using a block -- bounce between start position and (300,300) UIView.animateWithDuration(5.0, delay: 0.0,
  • ptions: .Repeat | .Autoreverse,
animations: { view.center = CGPoint(x: 300, y: 300) }, completion: nil)

UIKit

slide-20
SLIDE 20

OpenGL industry standard 2D/3D graphics API

slide-21
SLIDE 21

technically, OpenGL ES; 
 i.e., for Embedded Systems OpenGL

slide-22
SLIDE 22

OpenGL ES 2.0 not backwards compatible (fixed-function vs. shaders) OpenGL

slide-23
SLIDE 23

hardware acceleration iPad 2: CPUx2, GPUx9, iPad 3: CPUx1, GPUx2-3, etc. OpenGL

slide-24
SLIDE 24

OpenGL render destination: CAEAGLLayer in UIView OpenGL

slide-25
SLIDE 25

generally, don’t mix OpenGL and 
 UIKit/CA/CG functions (e.g., no layer transforms) OpenGL

slide-26
SLIDE 26

UIKit Core Graphics OpenGL ES Core Animation

slide-27
SLIDE 27

§ Drawing

slide-28
SLIDE 28

(0,0)

x y

320 480

“ULO”

slide-29
SLIDE 29

(0,0)

x y

320 480

“ULO”

slide-30
SLIDE 30

320 x 480 points (not necessarily = pixels!)

slide-31
SLIDE 31

≈ resolution independence scale factor × points = pixels (retina display: scale = 2.0)

slide-32
SLIDE 32

principal data types: CGPoint, CGSize, CGRect

/* Points. */ struct CGPoint { var x: CGFloat var y: CGFloat } /* Sizes. */ struct CGSize { var width: CGFloat var height: CGFloat } /* Rectangles. */ struct CGRect { var origin: CGPoint var size: CGSize }

slide-33
SLIDE 33

/* Return the left/mid/right x-value of ‘rect’. */ CGFloat CGRectGetMinX(CGRect rect); CGFloat CGRectGetMidX(CGRect rect); CGFloat CGRectGetMaxX(CGRect rect); /* Return the top/mid/bottom y-value of ‘rect’. */ CGFloat CGRectGetMinY(CGRect rect); CGFloat CGRectGetMidY(CGRect rect); CGFloat CGRectGetMaxY(CGRect rect); /* Return the width/height of ‘rect’. */ CGFloat CGRectGetWidth(CGRect rect); CGFloat CGRectGetHeight(CGRect rect); /* Standardize ‘rect’ -- i.e., convert it to an equivalent rect which has positive width and height. */ CGRect CGRectStandardize(CGRect rect); /* Return true if ‘rect’ is empty (that is, if it has zero width or height), false otherwise. A null rect is defined to be empty. */ bool CGRectIsEmpty(CGRect rect);

slide-34
SLIDE 34

locating/placing things: frame & bounds rectangles

slide-35
SLIDE 35

frame = origin & size in 
 superview’s coordinate system

slide-36
SLIDE 36

bounds = origin & size in 
 local view’s coordinate system

slide-37
SLIDE 37

application window

slide-38
SLIDE 38

application window

view frame:

  • origin = (6, 5)
  • size = (15, 10)

view

slide-39
SLIDE 39

application window view

view bounds:

  • origin = (0, 0)
  • size = (15, 10)

subview

subview frame:

  • origin = (3, 4)
  • size = (6, 5)
slide-40
SLIDE 40

size of frame, bounds are automatically linked

slide-41
SLIDE 41

reposition view by changing 
 frame origin or center (changing one automatically adjusts the other)

slide-42
SLIDE 42

can change bounds origin to adjust 
 local coordinate system

slide-43
SLIDE 43

view bounds:

  • origin = (0, 0)
  • size = (15, 10)

subview frame:

  • origin = (3, 4)
  • size = (6, 5)
slide-44
SLIDE 44

view bounds:

  • origin = (1, 1)
  • size = (15, 10)

subview frame:

  • origin = (3, 4)
  • size = (6, 5)
slide-45
SLIDE 45

view bounds:

  • origin = (-1, 4)
  • size = (15, 10)

subview frame:

  • origin = (3, 4)
  • size = (6, 5)
slide-46
SLIDE 46

/* Fill `rect' with solid color */ void UIRectFill(CGRect rect); void UIRectFillUsingBlendMode(CGRect rect, CGBlendMode blendMode); /* Draw 1px frame inside `rect'. */ void UIRectFrame(CGRect rect); void UIRectFrameUsingBlendMode(CGRect rect, CGBlendMode blendMode);

Simple Drawing

slide-47
SLIDE 47

@interface UIColor // Convenience methods for creating autoreleased colors + (UIColor *)colorWithRed:(CGFloat)red 
 green:(CGFloat)green 
 blue:(CGFloat)blue 
 alpha:(CGFloat)alpha; // Some convenience methods to create colors. These colors are cached. + (UIColor *)blackColor; // 0.0 white + (UIColor *)redColor; // 1.0, 0.0, 0.0 RGB + (UIColor *)greenColor; // 0.0, 1.0, 0.0 RGB + (UIColor *)blueColor; // 0.0, 0.0, 1.0 RGB + (UIColor *)clearColor; // 0.0 white, 0.0 alpha // Set the color: Sets the fill and stroke colors in the current drawing context.

  • (void)set;

// Set the fill or stroke colors individually.

  • (void)setFill;
  • (void)setStroke;

// Access the underlying CGColor @property(nonatomic,readonly) CGColorRef CGColor; @end

Color?

slide-48
SLIDE 48

UIKit framework always draws to implicit, current Graphics Context

slide-49
SLIDE 49

// establish current drawing context (image buffer) UIGraphicsBeginImageContext(CGSize(width: 100, height: 100)) // clear background with white box UIColor.whiteColor().set() UIRectFill(CGRect(x: 0, y: 0, width: 100, height: 100)) // draw black frame UIColor.blackColor().set() UIRectFrame(CGRect(x: 0, y: 0, width: 100, height: 100)) // draw (filled) blue rectangle UIColor.blueColor().set() UIRectFill(CGRect(x: 10, y: 10, width: 80, height: 80)) // extract image from context let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext()

slide-50
SLIDE 50

CG maintains a stack of graphics contexts (empty, by default)

slide-51
SLIDE 51

UIView objects automatically push graphics contexts before calling drawRect:

slide-52
SLIDE 52

@interface UIView(UIViewRendering) /* All custom drawing must happen from this method. Should limit drawing to ‘rect’ -- on first call ‘rect’ is usually equal to our bounds. */

  • (void)drawRect:(CGRect)rect;

/* drawRect: is called lazily. If view must be redrawn, we must notify the system by calling one of these methods below. */

  • (void)setNeedsDisplay;
  • (void)setNeedsDisplayInRect:(CGRect)rect;

@end

slide-53
SLIDE 53

Q: draw in current view vs. adding subview?

  • it depends …
  • subviews allow us to add/remove objects
  • but adds memory + processing overhead
slide-54
SLIDE 54

demo

HelloWorldView discuss root view frame origin/size Rectangle1 using setNeedsDisplay to force refresh Rectangle2 use multiple views to track drawn rectangles ignore contentStretch for now GameBoard
  • handling
slide-55
SLIDE 55

subview size > superview?

slide-56
SLIDE 56

default: container views don’t clip subviews … but no “scrolling”, either

  • to implement, parent may adjust its own

bounds to move children into view

  • or, alternatively, change all child frames

(much messier!)

slide-57
SLIDE 57

demo

Clipping Demo
  • effect of clipsToBounds
ScrollView Demo automatic pan support based on contentSize also: pinch to zoom
slide-58
SLIDE 58

view transformations: translating, scaling, rotating

slide-59
SLIDE 59

common strategy:

  • draw “unit” object at (0,0)
  • translate, scale, rotate to 


final position/size/orientation

slide-60
SLIDE 60

drawRect: can be very lazy i.e., draw once, transform many times

slide-61
SLIDE 61

implementation: “affine transform matrices”

slide-62
SLIDE 62

= ⇥ x + tx y + ty 1 ⇤ ⇥ x0 y0 1 ⇤ = ⇥ x y 1 ⇤ × 2 4 a b c d tx ty 1 3 5

transformation matrix transformed coordinates

  • riginal coordinates

x0 = ax + cy + tx y0 = bx + dy + ty ⇥ x0 y0 1 ⇤ = ⇥ x y 1 ⇤ × 2 4 1 1 tx ty 1 3 5 e.g.

slide-63
SLIDE 63

/* The identity transform: [ 1 0 0 1 0 0 ]. */ extern const CGAffineTransform CGAffineTransformIdentity; /* Return a transform which translates by `(tx, ty)': t' = [ 1 0 0 1 tx ty ] */ CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty); /* Return a transform which scales by `(sx, sy)': t' = [ sx 0 0 sy 0 0 ] */ CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy); /* Return a transform which rotates by `angle' radians: t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] */ CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle) /* Concatenate translation, scaling, rotation transforms to existing matrices. */ CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty); CGAffineTransform CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy); CGAffineTransform CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);

struct CGAffineTransform { CGFloat a, b, c, d; CGFloat tx, ty; }

slide-64
SLIDE 64

for given graphics context (e.g., in drawRect:), change current transform matrix (CTM)

slide-65
SLIDE 65

/* Scale the current graphics state's transformation matrix (the CTM) by `(sx, sy)'. */ void CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy); /* Translate the current graphics state's transformation matrix (the CTM) by `(tx, ty)'. */ void CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty); /* Rotate the current graphics state's transformation matrix (the CTM) by `angle' radians. */ void CGContextRotateCTM(CGContextRef c, CGFloat angle); /* Concatenate the current graphics state's transformation matrix (the CTM) with the affine transform `transform'. */ void CGContextConcatCTM(CGContextRef c, CGAffineTransform transform);

slide-66
SLIDE 66

demo

ExpandingFrame look at RotatingView drawRect motivate CGContextSave/RestoreGState explore frame/bounds relationship
slide-67
SLIDE 67

Drawing Other Shapes cubic & quadratic Bézier curves

slide-68
SLIDE 68

@interface UIBezierPath : NSObject<NSCopying, NSCoding> { + (UIBezierPath *)bezierPath;

  • (void)moveToPoint:(CGPoint)point;
  • (void)addLineToPoint:(CGPoint)point;
  • (void)addCurveToPoint:(CGPoint)endPoint

controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;

  • (void)addQuadCurveToPoint:(CGPoint)endPoint

controlPoint:(CGPoint)controlPoint;

  • (void)addArcWithCenter:(CGPoint)center

radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

  • (void)closePath;

+ (UIBezierPath *)bezierPathWithRect:(CGRect)rect; + (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect; + (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect 
 cornerRadius:(CGFloat)cornerRadius; @property(nonatomic) CGPathRef CGPath; @end

slide-69
SLIDE 69

Bézier curves can be created, stored, and reused, independent of graphics context

slide-70
SLIDE 70

demo

BezierDrawing
slide-71
SLIDE 71

rects, curves, etc. = vector graphics ⇒ infinite scaleability

slide-72
SLIDE 72

raster graphics? e.g., bitmaps, JPG, PNG

slide-73
SLIDE 73

UIKit: load with UIImage

slide-74
SLIDE 74

imageNamed:

Returns the image object associated with the specified filename. + (UIImage *)imageNamed:(NSString *)name Parameters name The name of the file. If this is the first time the image is being loaded, the method looks for an image with the specified name in the application’s main bundle. Return Value The image object for the specified file, or nil if the method could not find the specified image. Discussion This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object. On a device running iOS 4 or later, the behavior is identical if the device’s screen has a scale of 1.0. If the screen has a scale of 2.0, this method first searches for an image file with the same filename with an @2x suffjx appended to it. For example, if the file’s name is button, it first searches for button@2x. If it finds a 2x, it loads that image and sets the scale property of the returned UIImage object to 2.0. Otherwise, it loads the unmodified filename and sets the scale property to 1.0.

slide-75
SLIDE 75

caveat emptor: imageNamed cache can be
 quite aggressive! (Google: “imageNamed cache”)

slide-76
SLIDE 76

raster graphics problem: scaling → pixelation

slide-77
SLIDE 77

UIView contentStretch property defines which parts of an image are “stretched”

slide-78
SLIDE 78

demo

ContentStretching
slide-79
SLIDE 79

Image Drawing Options

image = UIImage(named:@"image.png")

let imageView = UIImageView(image: image) view.addSubview(imageView)

➊ UIKit (subview)

func drawRect(rect: CGRect) { image.drawAtPoint(CGPoint(x: 0, y: 0)) }

➋ CG (drawing)

let layer = CALayer() layer.contents = image.CGImage view.layer.addSublayer(layer)

➌ CA (compositing)

slide-80
SLIDE 80

demo

ImageDrawing run all three versions (view, layer, drawRect) --- may want to keep num_image < 100 for the last run through instruments: memory allocations (num_images=10000 for view, layer version), and time profiler (esp. interesting for drawRect based)
slide-81
SLIDE 81

more reading (Xcode library):

  • View Programming Guide for iOS
  • Quartz 2D Programming Guide
slide-82
SLIDE 82

§ Animation

slide-83
SLIDE 83

UIKit / CoreAnimation

slide-84
SLIDE 84

typically, use “magic” UIView / CALayer animation mechanism

slide-85
SLIDE 85

i.e., provide “new” view properties in animation block along with time frame — core animation does the rest

slide-86
SLIDE 86

note: CA updates happen in a separate thread!

slide-87
SLIDE 87

animated parameters are updated according to a specified interpolation curve (aka timing function)

slide-88
SLIDE 88

timing functions