Search code examples
iosswiftanimationdrawrectcgcontextdrawpath

Using drawRect to draw & animate two graphs. A background graph, and a foreground graph


I am using drawRect to draw a pretty simple shape (dark blue in the image below).

CGContextDrawPath I'd like this to animate from the left to the right, so that it grows. The caveat here is I need there to be a "max" background in gray, as seen in the top part of the image.

Right now, I'm simulating this animation by overlaying a white view, and then animating the size of it, so that it looks like the blue is animating to the right. While this works... I need the background gray shape to always be there. With my overlayed white view, this just doesn't work.

Here's the code for drawing the "current code" version:

    let context = UIGraphicsGetCurrentContext()
    CGContextMoveToPoint(context, 0, self.bounds.height - 6)
    CGContextAddLineToPoint(context, self.bounds.width, 0)
    CGContextAddLineToPoint(context, self.bounds.width, self.bounds.height)
    CGContextAddLineToPoint(context, 0, self.bounds.height)
    CGContextSetFillColorWithColor(context,UIColor(red: 37/255, green: 88/255, blue: 120/255, alpha: 1.0).CGColor)
    CGContextDrawPath(context, CGPathDrawingMode.Fill)

How can I animate the blue part from left to right, while keeping the gray "max" portion of the graph always visible?


Solution

  • drawRect is producing still picture. To get animation you're saying about I'd recommend the following:

    1. Use CoreAnimation to produce animation
    2. Use UIBezierPath to make a shape you need
    3. Use CALayer's mask to animate within required shape

    Here is example code for Playground:

    import UIKit
    import QuartzCore
    import XCPlayground
    
    let view = UIView(frame: CGRect(x: 0, y: 0, width: 120, height: 40))
    XCPlaygroundPage.currentPage.liveView = view
    
    let maskPath = UIBezierPath()
    
    maskPath.moveToPoint(CGPoint(x: 10, y: 30))
    maskPath.addLineToPoint(CGPoint(x: 10, y: 25))
    maskPath.addLineToPoint(CGPoint(x: 100, y: 10))
    maskPath.addLineToPoint(CGPoint(x: 100, y: 30))
    maskPath.closePath()
    
    let maskLayer = CAShapeLayer()
    maskLayer.path = maskPath.CGPath
    maskLayer.fillColor = UIColor.whiteColor().CGColor
    
    let rectToAnimateFrom = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 97, height: 40))
    let rectToAnimateTo = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 0, height: 40))
    
    let layerOne = CAShapeLayer()
    layerOne.path = maskPath.CGPath
    layerOne.fillColor = UIColor.grayColor().CGColor
    
    let layerTwo = CAShapeLayer()
    layerTwo.mask = maskLayer
    layerTwo.fillColor = UIColor.greenColor().CGColor
    
    view.layer.addSublayer(layerOne)
    view.layer.addSublayer(layerTwo)
    
    let animation = CABasicAnimation(keyPath: "path")
    animation.fromValue = rectToAnimateFrom.CGPath
    animation.toValue = rectToAnimateTo.CGPath
    animation.duration = 1
    animation.repeatCount = 1000
    animation.autoreverses = true
    
    layerTwo.addAnimation(animation, forKey: "Nice animation")