简体   繁体   中英

Faking UIView animation with drawRect

I have a UIView that draws itself using -drawRect: and I want to animate the colors used for the drawing. Basic UIView animation stuff doesn't work for obvious reasons ( drawRect ).

I can't simply use CAShapeLayer to draw and animate the view contents. I'd like to try faking the animation by hand, using a timer or CADisplayLink in combination with setNeedsDisplay . Is there a reasonably simple way to hide this magic behind the usual UIView animation API?

For example, let's say there's a color property I'd like to animate:

[UIView animateWithDuration:0.2 animations:^{
    [customDrawingView setColor:[UIColor redColor]];
}];

Is there a way to “intercept” the animation call to read the animation parameters (time, target value) and process them by hand? I don't want to swizzle UIView .

I had to do something like you did quite a few times. In general you can create some classes to handle stuff like floating point interpolation or CGPoint interpolation... and do it all properly but since the whole drawing already exists there is not much point in it.

So the UIView animateWithDuration will not work here but you can add your display link and do the interpolation manually. It is best done if no existing code is changed, only add a few methods:

Assuming you currently have something like this:

- (void)setColor:(UIColor *)color
{
    _color = color;
    [self setNeedsDisplay];
}

Now you can add setColorAnimated: and do all the additional functionality:

You need some additional parameters (properties), use what you want:

UIColor *_sourceColor;
UIColor *_targetColor;
NSDate *_startDate;
NSDate *_endDate;
CADisplayLink *_displayLink;

And the methods:

- (void)setColorAnimated:(UIColor *)color {
    _sourceColor = self.color; // set start to current color
    _targetColor = color; // destination color
    _startDate = [NSDate date]; // begins currently, you could add some delay if you wish
    _endDate = [_startDate dateByAddingTimeInterval:.3]; // will define animation duration

    [_displayLink invalidate]; // if one already exists
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onFrame)]; // create the display link
}
- (UIColor *)interpolatedColor:(UIColor *)sourceColor withColor:(UIColor *)targetColor forScale:(CGFloat)scale {
    // this will interpolate between two colors
    CGFloat r1, g1, b1, a1;
    CGFloat r2, g2, b2, a2;
    [sourceColor getRed:&r1 green:&g1 blue:&b1 alpha:&a1];
    [targetColor getRed:&r2 green:&g2 blue:&b2 alpha:&a2];

    // do a linear interpolation on RGBA. You can use other
    return [UIColor colorWithRed:r1+(r2-r1)*scale
                           green:g1+(g2-g1)*scale
                            blue:b1+(b2-b1)*scale
                           alpha:a1+(a2-a1)*scale];
}
- (void)onFrame {
    // scale is valid between 0 and 1
    CGFloat scale = [[NSDate date] timeIntervalSinceDate:_startDate] / [_endDate timeIntervalSinceDate:_startDate];
    if(scale < .0f) {
        // this can happen if delay is used
        scale = .0f;
    }
    else if(scale > 1.0f)
    {
        // end animation
        scale = 1.0f;
        [_displayLink invalidate];
        _displayLink = nil;
    }
    [self setColor:[self interpolatedColor:_sourceColor withColor:_targetColor forScale:scale]];
}

Animated (Fade in) new frame created via drawRect

- (void)drawRect:(CGRect)rect {
    CABasicAnimation *animation;
    animation = [CABasicAnimation animation];
    [animation setDuration:0.5];
    [[self layer] addAnimation:animation forKey:@"contents"];

   .. your stuff here

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM