1

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?

Joe
  • 3,772
  • 3
  • 33
  • 64

2 Answers2

1

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")
Joe
  • 3,772
  • 3
  • 33
  • 64
SVGreg
  • 2,320
  • 1
  • 13
  • 17
  • @SVGred - Wow, this is awesome! For the most part, this is exactly what i need! I'll need to modify it a little, but that's on me. With my initial implementation of the graph, i subclassed UIView with override func drawRect(rect: CGRect). Will I not need to override drawRect for your version? Also - can you explain the very last line? (layerTwo.addAnimation) I edited your post to swift 2.3 syntax as well. Plays nice on my end! – Joe Sep 12 '16 at 17:41
  • @SVGreg with multiple Swift versions being used, It would be nice to mention as to in which are you writing the answer. – Dravidian Sep 12 '16 at 21:31
  • 1
    @Joe: drawRect is required only to draw custom content inside view. The answer is - No - you do not need override *drawRect* to use my code. addAnimation is actually runs the animation you need. You can customize it to any animation you'd prefer. – SVGreg Sep 14 '16 at 16:15
  • @Dravidian The code is simple enough and does not use specific language instruction/features. But anyway - good suggestion. – SVGreg Sep 14 '16 at 16:18
  • @SVGreg Thanks for the follow up bud. I got it working to my desires just swimmingly. – Joe Sep 14 '16 at 16:19
0

In your code, I only see you draw the graphic once, why not draw gray part first and then draw the blue part.

I don't think it is efficient enough to implement animation in drawRect function.

You can take a look at Facebook's Shimmer Example, it simulate the effect of iPhone unlock animation. It uses a mask layer. The idea could also work in your example.

Also, Facebook's pop framework could simplify your work.

Yan Peng
  • 68
  • 6
  • "In your code, I only see you draw the graphic once, why not draw gray part first and then draw the blue part." - That's the plan, so long as I figure out a way to animate, or perhaps animate the mask of the blue layer. Thanks for the suggestions on the frameworks, but I want to keep this all native without any 3rd parties. – Joe Sep 12 '16 at 16:51