简体   繁体   中英

UIView with custom drawRect and animateWithDuration

I have a UIView subclass that displays circular progress:

循环进度视图

Screenshot above shows progress with value of 0.667 .

Inside my UIView subclass implementation in drawRect: method I am drawing two circles - one for background oval (gray), second for actual progress oval (blue). (Check out source code)

It works like expected, but doesn't allow to animate progress changes. I tried to find solution that allows using UIView 's animateWithDuration:animations: class method like this:

MyCustomView *progressView = ...
[UIView animateWithDuration:3 
                 animations:^{
                     progressView.progressValue = 1.f;
                 }];

But I wasn't able to make it animates that way.

You can check out the source code in my GitHub repository .

However, I found a way to perform an animation whenever progressValue changes. It's not exactly what I want (still not working with UIView 's animateWithDuration:animations: ), and I am not sure if this is the right direction. It involves subclassing CALayer of my UIView . You can check out the source code in the same repo mentioned above, on custom_layer branch . Here is direct link to CALayer subclass drawing implementation.

My question is: Having a UIView subclass that draws some shapes based on custom properties ( progressValue ), is it possible to make the view animates with UIView 's animateWithDuration:animations: when changing the view's property ( progressValue ) ?

UPDATE

Thanks to rounak's answer I get rid of drawing custom shapes in drawRect: method and created two separate CAShapeLayer s. Instead of drawing new shapes every time progressValue changes, I am just setting strokeEnd properties for that layers, which gives the same visual effect as before, but should perform better ( check out source code ).

Then I implemented actionForLayer:forKey: method ( check out source code ), which returns CABasicAnimation that should animate strokeEnd whenever progressValue changes.

However, I am still experiencing an issue with the animation:

__block NSDate *date = [NSDate date];
[UIView animateWithDuration:3
                 animations:^{
                     self.progressView.progressValue = 1.f;
                 }
                 completion:^(BOOL finished1) {
                     NSLog(@"first animation completed in %f", [[NSDate date] timeIntervalSinceDate:date]);
                     date = [NSDate date];
                     [UIView animateWithDuration:3
                                      animations:^{
                                          self.progressView.progressValue = 0.f;
                                      }
                                      completion:^(BOOL finished2) {
                                          NSLog(@"second animation completed in %f", [[NSDate date] timeIntervalSinceDate:date]);
                                          [self animateProgress];
                                      }];
                 }];

My circular progress view animates progressValue changes, and executing the code above tells that each animation takes about 3 seconds. Unfortunately, on screen the view animates very fast (animation completes in fraction of a second). Here is what it looks like:

在此输入图像描述

I am not sure where the problem is, but I noticed that changing keypath in [CABasicAnimation animationWithKeyPath:@"strokeEnd"] from strokeEnd to anything else doesn't gives any effect (animation always looks the same).

Any hints of what I am missing here will be appreciated.

UPDATE 2

I have just noticed that animation is in fact triggered by those two lines in setProgressValue: setter:

self.progressOvalLayer.strokeEnd = MIN(1, MAX(0, progressValue));
self.backgroundOvalLayer.strokeEnd = MIN(1, MAX(0, 1 - progressValue));

( see on GitHub )

It looks like my implementation of actionForLayer:forKey: does not work at all, despite being executed (checked with brakepoint).

I wonder what I am doing wrong?

UPDATE - SOLUTION FOUND

It occurred that I tried to animate main layer of my UIView instead of custom layers that contains oval shapes. I needed to fix actionForLayer:(CALayer *)layer forKey: implementation to make it works ( check out fixed source code ). That solves my problem.

I'd suggest you draw the circular progress with a CAShapeLayer , and use the strokeStart , strokeEnd , and fillColor properties to achieve your effect. You'll likely need two shapeLayers, one over the other, but the advantage with this approach is that shapeLayer's properties are animateable via CoreAnimation.

Additionally, you can use hackery like this to make it animate within animation blocks as well https://gist.github.com/nicklockwood/d374033b27c62662ac8d

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