13

Even though I have asked this question before, I would like to reach out again to clarify what I would like to accomplish with your help. I was wondering how you would create a background of and iOS application in xCode similar to the background of the Solar weather app (screenshots provided) that changes slightly over time (in a cycle). As you can see the gradient is very smooth and obviously contains more than two main points. Any help with example or snippets of code would be greatly appreciated. Thanks Again, Ben.

Screenshot 1 Screenshot 2

Ben Leather
  • 233
  • 1
  • 2
  • 12

2 Answers2

16

What I need is:

  1. adding a CAGradientLayer to your view controller (or custom view), e.g.:

    @property (nonatomic, retain) CAGradientLayer *gradient;
    
  2. In your view controller, in viewDidLoad, create and add the gradient layer to your view:

    self.gradient = [CAGradientLayer layer];
    gradient.frame = self.view.bounds;
    gradient.colors = [NSArray arrayWithObjects:
                   (id)topColor.CGColor,
                   (id)bottomColor.CGColor,
                   nil];
    gradient.locations = [NSArray arrayWithObjects:
                      [NSNumber numberWithFloat:0.0f],
                      [NSNumber numberWithFloat:0.7],
                      nil];
    [self.view.layer addSublayer:self.gradient];
    

    3a. add an NSTimer to your controller that will update self.gradient at proper intervals by doing:

      topColor = ...; bottomColor = ...;
      NSArray* newColors = [NSArray arrayWithObjects:
                     (id)topColor.CGColor,
                     (id)bottomColor.CGColor,
                     nil];
     [(CAGradientLayer *)self.layer setColors:newColors];
    

    This will allow you to exactly decide which colour you want to use for the gradient at each step. Otherwise, you might try with an animation, like this:

    3b. add the animation like this,

    - (void)animateLayer... {
    
      [UIView animateWithDuration:duration 
                            delay:0
                          options:UIViewAnimationOptionCurveEaseInOut
                       animations:^{
        [CATransaction begin];
        [CATransaction setAnimationDuration:duration];
    
          topColor = ...; bottomColor = ...;
          NSArray* newColors = [NSArray arrayWithObjects:
                     (id)topColor.CGColor,
                     (id)bottomColor.CGColor,
                     nil];
         [(CAGradientLayer *)self.layer setColors:newColors];
    
         [CATransaction commit];
      }
              completion:^(BOOL b) {
                  [self animateLayer..];
              }];
    }
    

You might also combine 3a and 3b.

Tyson Vignesh
  • 315
  • 2
  • 14
sergio
  • 68,819
  • 11
  • 102
  • 123
  • How and where would implement 3b? – Ben Leather Feb 17 '13 at 17:10
  • 3b already provides you an animation of a given duration; so you just pack it into a method and call it (ie. from viewDidLoad); you need then to provide a mechanism so that it is repeated after `duration` seconds; for that you could use a timer, or use the `completion` block for the animation. I have expanded my answer to take this into account. – sergio Feb 17 '13 at 17:22
  • 1
    This code, when implemented as you said comes up with a number of errors: Use of undeclared identifier, topColor; Property 'bounds' not found on object type "View Controller *" for example. – Ben Leather Feb 17 '13 at 23:22
  • 3
    yeah, that is just a snippet of code describing the key points that would allow you to solve your problem; it is not a framework or a full class that you can think of using as it is. you should fill up the missing bits, like the colors, and define properly your class so that it uses the provided snippet in a way that makes sense for your app (which I don't know, so I could not do it for you). – sergio Feb 18 '13 at 08:42
  • as to the `bounds` property it should be referred to `self.view` instead of `self` (i.e, `self.view.bounds`). the code came from a custom view I have, and I adapted it to be used inside of a UIViewController for your question, so you might find more oversights like that. – sergio Feb 18 '13 at 08:43
  • Even when implementing this like you said there are a multitude of errors that need to be addressed (probably my fault) - but is there an example project which I can look at. This would be a great help. – Ben Leather Feb 18 '13 at 10:54
  • I have edited the question for a clearer description of what I was asking for help with. – Ben Leather Feb 19 '13 at 10:32
  • @sergio how can i implement the repeat animation process in swift ? thanks – DrPatience Aug 12 '15 at 09:19
13

Simple working example:

.h file

@property (strong, nonatomic) CAGradientLayer *gradient;

.m file

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear: animated];

    self.gradient = [CAGradientLayer layer];
    self.gradient.frame = self.view.bounds;
    self.gradient.colors = @[(id)[UIColor purpleColor].CGColor,
                             (id)[UIColor redColor].CGColor];

    [self.view.layer insertSublayer:self.gradient atIndex:0];

    [self animateLayer];
}

-(void)animateLayer
{

    NSArray *fromColors = self.gradient.colors;
    NSArray *toColors = @[(id)[UIColor redColor].CGColor,
                          (id)[UIColor orangeColor].CGColor];

    [self.gradient setColors:toColors];

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"colors"];

    animation.fromValue             = fromColors;
    animation.toValue               = toColors;
    animation.duration              = 3.00;
    animation.removedOnCompletion   = YES;
    animation.fillMode              = kCAFillModeForwards;
    animation.timingFunction        = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    animation.delegate              = self;

    // Add the animation to our layer

    [self.gradient addAnimation:animation forKey:@"animateGradient"];
}

You might want to implement an additional method to shift the colors, and then reanimate the changes. But this should help you get there.

nmdias
  • 3,888
  • 5
  • 36
  • 59
  • I have been able to apply this in swift, however when the animation is completed it changes back to the first color. How do I convert this line in swift [self.gradient setColors:toColors] ? thanks – DrPatience Jul 22 '15 at 09:37
  • 2
    Simple: `self.gradient.colors = toColors` – TheTiger Jul 22 '15 at 10:09
  • It's also a good practice to include a call to `super` in your `viewDidAppear`. – smaili Apr 04 '16 at 17:18