How to fade UIView background color - iOS 5

2019-04-15 18:05发布

问题:

I'm having trouble trying to get a custom UIView class to fade it's background. I've checked out the following StackOverflow questions but they don't seem to work for me.

Fade background-color from one color to another in a UIView

How do you explicitly animate a CALayer's backgroundColor?

So I have a custom UIView where users can draw things. If what they draw is incorrect, I want to make the background color fade to red then back to white.

I have this custom method in the custom UIView called

- (void)indicateMistake;

When I call that method I want it to perform the background color animation, so i have this in the method:

CABasicAnimation* fade = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
fade.fromValue = (id)[UIColor whiteColor].CGColor;
fade.toValue = (id)[UIColor redColor].CGColor;
[fade setDuration:3];
[self.layer addAnimation:fade forKey:@"fadeAnimation"];

But nothing seems to happen when I do that.

So then I tried a silly rotation animation to see if it even works:

CABasicAnimation* rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotate.toValue = [NSNumber numberWithInt:M_PI];
[rotate setDuration:1];
[self.layer addAnimation:rotate forKey:@"rotateAnimation"];

For some reason that works and the custom UIView rotates.

Then reading more StackOverflow answers I tried this:

[UIView animateWithDuration:3 animations:^{
    [self setBackgroundColor: [UIColor redColor]];
} completion:^(BOOL finished) {
    [self setBackgroundColor: [UIColor whiteColor]];
}];

That changes the color from red then back to white immediately. Sometimes It's so fast I sometimes can't see it happen. If i comment out the [self setBackgroundColor: [UIColor whiteColor]]; it stays red. But There's node gradual white to red effect.

I've ran out of ideas.. Any help would be appreciated!

回答1:

So I have a custom UIView where users can draw things.

I'm assuming that means you've implemented -drawRect:.

IIRC, by default (if UIView.clearsContextBeforeDrawing is set), the context gets filled with the view's background colour, -drawRect: gets called, and the resulting bitmap is drawn on screen instead of the background colour. Animating this would mean animating between the two bitmaps, which isn't something Core Animation is particularly good at. (I could be wrong about the details here, but it explains the behaviour).

The easiest fix is to set the background colour to [UIColor clearColor] and animate the background colour of a view behind the one you are drawing to. This means the view will not need to be redrawn, only recomposited over the "background".



回答2:

[UIView animateWithDuration:0.3f animations:^{
   fade.layer.backgroundColor = [UIColor redColor].CGColor;
}

That assumes you have pre-set your fade view to the white color that you want prior.

The reason your last attempt at an animation jumps back to white is because in your completion clause of animateWithDuration, you're setting the view back to white.

That completion clause executes when the animation is finished, so the animation (white-red) occurs, then your compiler goes to complete and jumps the fade view back to white.

As an added bonus:

I'm sure you might be wondering how you can go back to white.

Well, lets assume that the pressing of a UIButton calls this function called, "animateColors". So with that said, simply implement a BOOL.

- (void)animateColors {        
    if (!isRed) {
        [UIView animateWithDuration:0.3f animations:^{
            fade.layer.backgroundColor = [UIColor redColor].CGColor;
        }];
        isRed = YES;
    } else if (isRed) {
        [UIView animateWithDuration:0.3f animations:^{
            fade.layer.backgroundColor = [UIColor whiteColor].CGColor;
        }];
        isRed = NO;
    }
}

Obviously, you're going to want to make a property declaration of the boolean, isRed, in you interface or header file.



回答3:

I tried your code, as is. It worked perfectly - it faded from my view's original white to red, slowly (and then jumped back to white, which is what you'd expect). So if this isn't working for you, something else is going on.

A very useful diagnostic tool is to extract your troublesome code and make a project consisting of nothing else. What I did was what you should do. Make a completely new Single View Application project. Now you have a view controller and its view. Implement this method:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    CABasicAnimation* fade = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
    fade.fromValue = (id)[UIColor whiteColor].CGColor;
    fade.toValue = (id)[UIColor redColor].CGColor;
    [fade setDuration:3];
    [self.view.layer addAnimation:fade forKey:@"fadeAnimation"];
}

Run the project. The fade works perfectly. So you see, whatever is going wrong is not in this code! It is somewhere else. Perhaps you have other code that is also doing things to the color - a different animation that is crushing this one. Perhaps some reference is not referring to what you think it is. Perhaps something is covering the layer so you can't see it (a sublayer). Who knows? You didn't show your whole project (nor should you). The important thing is to prove to yourself that the code should work so you can figure out what's really wrong. And I've shown you how to do exactly that.