81

This looks like it should work, but doesn't. The color turns green at once.

self.labelCorrection.backgroundColor = [UIColor whiteColor];
[UIView animateWithDuration:2.0 animations:^{
    self.labelCorrection.backgroundColor = [UIColor greenColor];
}];
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Michiel de Mare
  • 41,982
  • 29
  • 103
  • 134
  • 2
    For any future readers, this is not specific to UILabel, but the requirement is that a view must start with a background color, even if that color is clear. You do not need to animate on the layer, the UIView.backgroundColor works fine, provided the above. – Chris Conover Feb 01 '18 at 20:36

11 Answers11

149

I can't find it documented anywhere, but it appears the backgroundColor property of UILabel is not animatable, as your code works fine with a vanilla UIView. This hack appears to work, however, as long as you don't set the background color of the label view itself:

#import <QuartzCore/QuartzCore.h>

...

theLabel.layer.backgroundColor = [UIColor whiteColor].CGColor;

[UIView animateWithDuration:2.0 animations:^{
    theLabel.layer.backgroundColor = [UIColor greenColor].CGColor;
} completion:NULL];
bosmacs
  • 7,341
  • 4
  • 31
  • 31
  • This works, thank you! I'd still like to know why a UIView works, and a UILabel doesn't, after all, a UILabel is a UIView. – Michiel de Mare Sep 21 '10 at 19:05
  • 4
    Agreed. I can only assume at this point that Apple chose to make those properties non-animated for `UILabel`. FWIW, `UIButton` behaves the same way as the label. – bosmacs Sep 21 '10 at 19:13
  • 2
    Strangely enough, at least the `frame` property seems to be not animatable too if the `UILabel` comes from a xib. It does work if it is created programmatically, though... weird. – Julian D. Aug 08 '11 at 06:02
  • 1
    UITextFields behave similarly—no animation. – Robert Atkins Nov 28 '11 at 16:09
  • 1
    All I get is a white background with this on iOS 6. – Ethan Mick Apr 09 '13 at 19:29
  • wayfarer: you need to set the backgroundColor of the UILabel to ClearColor or else it will just animate the stuff behind the label, if that makes sense. Also, this works but is hackish, cause the corners of the rounded UILabel show up while its animating, and it looks bad =/ – mgrandi May 29 '13 at 02:10
  • Do you know why it is the case ? – Paweł Brewczynski Mar 14 '14 at 07:29
  • I'm getting a white background on iOS 7 as well. – eladleb Mar 27 '14 at 19:23
  • Also tested and confirmed on UITextView. Saved me! :) – Flying_Banana Oct 15 '14 at 09:00
  • I just noticed that even if you set the background color of the layer of the UILabel, it can still doesn't work. First I was setting the backgroundColor of my UILabel in my init method, and then animate the layer, but it didn't work, so we must be careful at the init of the UILabel : we must set its **layer** backgroundColor, not its backgroundColor. – AnthonyR Mar 04 '16 at 18:46
  • [It looks like](https://developer.apple.com/reference/uikit/uiview/1622591-backgroundcolor) it's now animatable, but it still doesn't work :( Also, I always thought that you can only animate `layer`'s properties using `CAAnimation` and not `UIView animate...` functions. Did I live in a lie? – Iulian Onofrei Oct 25 '16 at 15:02
25

Swift

  1. (important) Set the UILabel's background color to clear (either in IB or in code). For example:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        myLabel.backgroundColor = UIColor.clear
    }
    
  2. Animate the layer background color.

    @IBAction func animateButtonTapped(sender: UIButton) {
    
        UIView.animate(withDuration: 1.0, animations: {
            self.myLabel.layer.backgroundColor = UIColor.red.cgColor
        })
    }
    

Note that CGColor is added after the UIColor.

Result

enter image description here

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
10

Swift 3

To animate your label background color from white to green, set up your label like this:

self.labelCorrection.backgroundColor = UIColor.clear
self.labelCorrection.layer.backgroundColor = UIColor.white.cgColor

Animate like this:

UIView.animate(withDuration: 2.0) { 
    self.labelCorrection.layer.backgroundColor = UIColor.green.cgColor
}

To go back to the original state, so you can animate again, make sure you remove animations:

self.labelCorrection.layer.backgroundColor = UIColor.white.cgColor
self.labelCorrection.layer.removeAllAnimations()
Mike Taverne
  • 9,156
  • 2
  • 42
  • 58
7

You CAN animate it, but I had to first set it (programmatically) to clearColor for some reason. Without that, the animation either didn't work or wasn't visible.

I am animating the background color of a UILabel in a custom table cell. Here is the code in the willDisplayCell method. I wouldn't try to set the animation in cellForRow, since a lot of the layout gets tinkered with by the table.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
        ((RouteListCell *)cell).name.backgroundColor = [UIColor clearColor];
        [((RouteListCell *)cell).name backgroundGlowAnimationFromColor:[UIColor whiteColor] toColor:[UIColor redColor] clearAnimationsFirst:YES];
}

Here's my animation routine:

-(void) backgroundGlowAnimationFromColor:(UIColor *)startColor toColor:(UIColor *)destColor clearAnimationsFirst:(BOOL)reset;
{
    if (reset)
    {
        [self.layer removeAllAnimations];
    }

    CABasicAnimation *anAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
    anAnimation.duration = 1.00;
    anAnimation.repeatCount = HUGE_VAL;
    anAnimation.autoreverses = YES;
    anAnimation.fromValue = (id) startColor.CGColor; // [NSNumber numberWithFloat:1.0];
    anAnimation.toValue = (id) destColor.CGColor; //[NSNumber numberWithFloat:0.10];
    [self.layer addAnimation:anAnimation forKey:@"backgroundColor"];
}
software evolved
  • 4,314
  • 35
  • 45
2

Please try this,

[UIView transitionWithView:yourView duration:0.2 options:UIViewAnimationOptionTransitionNone animations:^{

[yourView setBackgroundColor:[UIColor colorWithRed:0.8588 green:0.8588 blue:0.8588 alpha:1]];
    }completion:^(BOOL finished) {

 }];

It works for me.

bummi
  • 27,123
  • 14
  • 62
  • 101
Techie
  • 143
  • 2
  • 9
2

I recently came across this problem once again and after some research I figured that basically nothing changed (and probably won't in foreseeable future), so I ended up using Facebook POP framework (not only) for that.

You can easily do animations of basic properties on both view and layer, but in addition, it provides smooth transition between colors (and basically whatever you want, even your custom types, with some additional coding). So for background color, you would do something like this:

   // Create spring animation (there are more types, if you want)
   let animation = POPSpringAnimation(propertyNamed: kPOPViewBackgroundColor)

   // Configure it properly
   animation.autoreverses = false
   animation.removedOnCompletion = true
   animation.fromValue = UIColor.yourBeginningColor()
   animation.toValue = UIColor.yourFinalColor()

   // Add new animation to your view - animation key can be whatever you like, serves as reference
   label.pop_addAnimation(animation, forKey: "YourAnimationKey")

Viola, now you can animate background colors on everything that has that property!

Jiri Trecak
  • 5,092
  • 26
  • 37
2

Do the color animation manually, based off of an NSTimer or CADisplayLink callback at some reasonable frame rate (say 20 fps). You will have to calculate your own color change curve in RGB or HSV based on the fractional elapsed time over the full animation time (2.0 seconds in your example), or use an array of 40 intermediate colors.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
2

Swift 4.1 UILabel Extension:

Add this extension to your code:

extension UILabel {

    /** Animates the background color of a UILabel to a new color. */
    func animateBackgroundColor(to color : UIColor?, withDuration : TimeInterval, completion : ((Bool) -> Void)? = nil) {
        guard let newColor = color else { return }
        self.backgroundColor = .clear
        UIView.animate(withDuration: withDuration, animations: {
            self.layer.backgroundColor = newColor.cgColor
        }, completion: completion)
    }

}

You should use it like this:

myUILabel.animateBackgroundColor(to: .red, withDuration: 0.5)

Now, if you want to restore the original color, use it like this:

// Storing the orignal color:
let originalColor = myUILabel.backgroundColor

// Changing the background color:
myUILabel.animateBackgroundColor(to: .red, withDuration: 0.5)

// ... Later:

// Restoring the original color:
myUILabel.animateBackgroundColor(to: originalColor, withDuration: 0.5, completion: {
    (value: Bool) in
    myUILabel.backgroundColor = originalColor
})
ThiagoAM
  • 1,432
  • 13
  • 20
1

it says in the docs that backgroundColor is animatable

http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-SW131

Don't really know since when.

hfossli
  • 22,616
  • 10
  • 116
  • 130
1

If you don't want to dip down into Quartz, per answer one of the prev answers, create an empty UIView of same size as UILabel and position it in same spot as the UILabel (and in Z order, under it) and you can animate that UIView's background color (I've tried this, and it works for me).

David Neiss
  • 8,161
  • 2
  • 20
  • 21
0

Here is the reference:

animations A block object containing the changes to commit to the views. This is where you programmatically change any animatable properties of the views in your view hierarchy. This block takes no parameters and has no return value. This parameter must not be NULL

What I understand about this is when your view call the animation block, then your view label background color is commited to be green since that time. Then, if you don't change the label background color to something else, then it will always be green. This is what I guess

vodkhang
  • 18,639
  • 11
  • 76
  • 110