Supereid86
Supereid86

Reputation: 85

How to animate gradient moving up on iOS

I want to mimic the "Want" iPhone app background, there are 3 colors that I want a view to cycle through, let us say red, blue, and green.

I want there to be a gradient with red to blue to green, each 1/3 of the screen, faded into each other (gradient), then, I want the red to go off screen on the top and come back on the bottom. (See photos below...)

The animation should move up, I want the gradient to go up and reform at the bottom of the screen and go up.

I tried using CAGradientLayer and animating the "colors" property, but that just looks like everything fades into each other, not necessarily moving off screen.

Contemplating using OpenGL, but don't want to go that low for something that seems pretty simple. Any help/code samples would be much appreciated. I basically need help animating a gradient using CoreAnimation/CoreGraphics.

Thanks in advance!

enter image description here

enter image description here

enter image description here

Upvotes: 6

Views: 3842

Answers (2)

Mike M
Mike M

Reputation: 5122

You should subclass UIView to create a GradientView class. Add an array to hold the colors you are moving through. The choice is important and I wrote an article explaining why. Override the drawRect(rect: CGRect) method and interpolate between the components of the colors:

override func drawRect(rect: CGRect) {

   let context = UIGraphicsGetCurrentContext();
   CGContextSaveGState(context);

   let c1 = colors[index == 0 ? (colors.count - 1) : index - 1] // previous
   let c2 = colors[index]                                       // current
   let c3 = colors[index == (colors.count - 1) ? 0 : index + 1] // next

   var c1Comp = CGColorGetComponents(c1.CGColor)
   var c2Comp = CGColorGetComponents(c2.CGColor)
   var c3Comp = CGColorGetComponents(c3.CGColor)

   var colorComponents = [
       c1Comp[0] * (1 - factor) + c2Comp[0] * factor, // color 1 and 2
       c1Comp[1] * (1 - factor) + c2Comp[1] * factor,
       c1Comp[2] * (1 - factor) + c2Comp[2] * factor,
       c1Comp[3] * (1 - factor) + c2Comp[3] * factor,
       c2Comp[0] * (1 - factor) + c3Comp[0] * factor, // color 2 and 3
       c2Comp[1] * (1 - factor) + c3Comp[1] * factor,
       c2Comp[2] * (1 - factor) + c3Comp[2] * factor,
       c2Comp[3] * (1 - factor) + c3Comp[3] * factor    
   ]

   let gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), &colorComponents, [0.0, 1.0], 2)

   let startPoint = CGPoint(x: 0.0, y: 0.0)
   let endPoint = CGPoint(x: rect.size.width, y: rect.size.height)
   CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, CGGradientDrawingOptions.DrawsAfterEndLocation)

   CGContextRestoreGState(context);
}

The index variable starts from 0 and wraps around the colors array while the factor is between 0.0 and 1.0 and animates through a timer:

var index: Int = 0
var factor: CGFloat = 1.0
var timer: NSTimer?

func animateToNextGradient() {
    index = (index + 1) % colors.count
    self.setNeedsDisplay()
    self.factor = 0.0    
    self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0/60.0, target: self, selector: "animate:", userInfo: nil, repeats: true)

}

func animate(timer: NSTimer) {
    self.factor += 0.02
    if(self.factor > 1.0) {
        self.timer?.invalidate()
    }
    self.setNeedsDisplay()
}

Upvotes: 0

jrturton
jrturton

Reputation: 119242

Animating the colors property of a CAGradientLayer will just cause a fade, as you've discovered. However I think a gradient layer is the way to go. You should look at the following options:

  • animating the positions property as well as / instead of the colors.
  • creating a larger gradient layer that contains your full animation cycle and animating its position

The second option will probably give the best performance as the gradient will not need to be recalculated.

Upvotes: 6

Related Questions