Upgrade to Pro — share decks privately, control downloads, hide ads and more …

To drawRect or not to drawRect (MobOS 2015)

sammyd
February 19, 2015

To drawRect or not to drawRect (MobOS 2015)

A review of the different options for drawing graphics on iOS, including UIKit, Core Graphics and Core Animation.
This is the version presented at Romanian MobOS Con 2nd edition.

sammyd

February 19, 2015
Tweet

More Decks by sammyd

Other Decks in Programming

Transcript

  1. How do the bits fit together? UIView Hierarchy Graphics Hardware

    via CALayer rotate 30° opacity 60% backed by drawRect:
  2. So what actually is drawRect? ★ UIView backed by CALayer

    ★ Implementation of CALayerDelegate method drawLayer:inContext: ★ Informal protocol ★ Use to provide layer content and arrange sublayers
  3. SCArrowView protocol @protocol SCArrowView <NSObject>
 
 - (instancetype)initWithFrame:(CGRect)frame
 from:(CGPoint)from
 to:(CGPoint)to;

    @property (nonatomic, assign) CGPoint from;
 @property (nonatomic, assign) CGPoint to;
 @property (nonatomic, assign) CGFloat headSize;
 @property (nonatomic, assign) CGFloat lineThickness;
 @property (nonatomic, assign) CGFloat bendiness;
 @property (nonatomic, strong) UIColor *color; - (void)redrawArrow; @end
  4. ★UIView = blank canvas ★Various subclasses for creating content ★UIImageView

    displays images ★Highly optimised for this purpose UIImageView
  5. UIImageView Arrow if(! self.imageView && self.image) {
 self.imageView = [[UIImageView

    alloc] initWithImage:self.image];
 [self addSubView:self.imageView];
 } from to
  6. UIImageView Arrow CGPoint ivCentre = CGPointZero;
 ivCentre.x = (self.from.x +

    self.to.x) / 2.0;
 ivCentre.y = (self.from.y + self.to.y) / 2.0;
 self.imageView.center = ivCentre; from to
  7. UIImageView Arrow CGFloat arrowLength = sqrt(pow((self.to.y - self.from.y),2) +
 pow((self.to.x

    - self.from.x),2) );
 CGRect arrowBounds = self.imageView.bounds;
 arrowBounds.size.width = arrowLength;
 self.imageView.bounds = arrowBounds; from to
  8. UIImageView Arrow CGFloat arrowLength = sqrt(pow((self.to.y - self.from.y),2) +
 pow((self.to.x

    - self.from.x),2) );
 CGRect arrowBounds = self.imageView.bounds;
 arrowBounds.size.width = arrowLength;
 self.imageView.bounds = arrowBounds; from to
  9. UIImageView Arrow from to CGFloat arrowAngle = atan2((self.to.y - self.from.y),


    (self.to.x - self.from.x));
 self.arrowImageView.transform =
 CGAffineTransformMakeRotation(arrowAngle);
  10. UIImageView Arrow from to UIImage *preColoredArrow = [self preColoredArrowImage];
 self.tintColor

    = self.color;
 self.image = [preColoredArrow
 imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
 self.arrowImageView.image = self.image;
  11. CoreGraphics Arrow CGContextMoveToPoint(cxt, start.x, start.y); CGContextAddQuadCurveToPoint(cxt, control.x, control.y, end.x, end.y);

    CGContextMoveToPoint(cxt, arrowSide1.x, arrowSide1.y); CGContextAddLineToPoint(cxt, arrowSide2.x, arrowSide2.y); CGContextAddLineToPoint(cxt, end.x, end.y); CGContextStrokePath(cxt); CGContextRef cxt = UIGraphicsGetCurrentContext();
  12. Adding animation - (void)animationUpdate:(CADisplayLink *)sender
 {
 self.animating = YES
 if(!self.timer)

    {
 self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationUpdate:)];
 [self.timer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
 self.animationStartTime = CACurrentMediaTime();
 } 
 CGFloat propComplete = (CACurrentMediaTime() - self.animationStartTime) / self.animationTime;
 if(propComplete >= 1) {
 self.currentEnd = self.to;
 [self.timer invalidate];
 self.timer = nil;
 } else {
 self.currentEnd = CGPointMake((self.to.x - self.from.x) * propComplete + self.from.x,
 (self.to.y - self.from.y) * propComplete + self.from.y);
 } 
 [self setNeedsDisplay];
 }
  13. Presentation Tree Render Tree Application Framework ★ CALayer is the

    fundamental object ★ Exists in a hierarchy ★ 3 distinct hierarchies Model Tree CoreAnimation
  14. Providing Content to CALayer ★ contents property ★ drawLayer:inContext: method

    on CALayerDelegate ★ Subclass and override drawing methods - drawInContext: or display ★ Specialist subclasses of CALayer
  15. Creating Arrow Path self.arrowPathLayer = [CAShapeLayer layer];
 [self.layer addSublayer:self.arrowPathLayer]; self.arrowPathLayer.bounds

    = self.bounds;
 self.arrowPathLayer.position = CGPointMake(CGRectGetMidX(self.bounds),
 CGRectGetMidY(self.bounds)); self.arrowPathLayer.strokeColor = self.color.CGColor;
 self.arrowPathLayer.lineWidth = self.lineThickness; UIBezierPath *path = [self.arrowPath arrowBezierPath];
 self.arrowPathLayer.path = path.CGPath;
  16. Creating Arrow Head self.arrowHeadLayer = [CAShapeLayer layer];
 [self.layer addSublayer:self.arrowHeadLayer]; self.arrowHeadLayer.bounds

    = self.bounds;
 self.arrowHeadLayer.position = CGPointMake(CGRectGetMidX(self.bounds),
 CGRectGetMidY(self.bounds)); self.arrowHeadLayer.strokeColor = self.color.CGColor;
 self.arrowHeadLayer.lineWidth = self.lineThickness; UIBezierPath *headPath = [self arrowHeadBezierPath]; self.arrowHeadLayer.path = headPath.CGPath;
  17. CAAnimation CALayer Implicit backgroundColor borderColor borderWidth bounds cornerRadius mask opacity

    position transform … CAGradientLayer colors locations startPoint endPoint CAShapeLayer lineDashPhase fillColor path* strokeStart strokeEnd … CATextLayer fontSize* foregroundColor* CAReplicatorLayer instanceAlphaOffset instanceColor instanceDelay … CAShapeLayer lineDashPhase fillColor path* strokeStart
 strokeEnd
 …
  18. Stroke Animation 0 1 CABasicAnimation *animation = [CABasicAnimation
 animationWithKeyPath:@"strokeEnd"];
 animation.duration

    = 2;
 animation.fromValue = @0;
 animation.toValue = @1;
 animation.timingFunction = [CAMediaTimingFunction
 functionWithName:kCAMediaTimingFunctionEaseIn];
  19. Arrow Head Animation CABasicAnimation *headAnimationLeft = [CABasicAnimation
 animationWithKeyPath:@"strokeEnd"];
 headAnimationLeft.duration =

    1.0;
 headAnimationLeft.fromValue = @0.5;
 headAnimationLeft.toValue = @1;
 headAnimationLeft.beginTime = 2;
 headAnimationLeft.timingFunction = [CAMediaTimingFunction
 functionWithName:kCAMediaTimingFunctionEaseOut]; CABasicAnimation *headAnimationRight = [CABasicAnimation
 animationWithKeyPath:@"strokeStart"];
 headAnimationRight.duration = 1.0;
 headAnimationRight.fromValue = @0.5;
 headAnimationRight.toValue = @0;
 headAnimationRight.beginTime = 2;
 headAnimationRight.timingFunction = [CAMediaTimingFunction
 functionWithName:kCAMediaTimingFunctionEaseOut]; 1 0.5 0 1 0.5 0
  20. Arrow Head Animation CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
 animationGroup.duration =

    3;
 animationGroup.animations = @[headHidden, headAnimationRight,
 headAnimationLeft]; [self.arrowPathLayer addAnimation:animation
 forKey:@"SCArrowDrawAnimation"];
 [self.arrowHeadLayer addAnimation:animationGroup
 forKey:@"SCArrowDrawHeadAnimation"];
  21. ★ UIImageView is mega simple ★ Use UIBezierPath to draw

    shapes ★ Don’t try and animate with drawRect ★ Discover the CALayer subclasses ★ CALayer offers software design advantages ★ CALayer when interaction not required in summary