ios – 使用自定义drawRect和animateWithDuration的UIView

我有一个显示循环进度的UIView子类:

上面的屏幕截图显示了值为0.667的进度.

在drawRect:方法中我的UIView子类实现内部我绘制了两个圆 – 一个用于背景椭圆(灰色),第二个用于实际进度椭圆(蓝色). (Check out source code)

它的工作方式与预期相同,但不允许对进度更改进行动画处理.我试图找到允许使用UIView的animateWithDuration的解决方案:动画:这样的类方法:

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

但我无法以这种方式制作动画.

您可以在my GitHub repository查看源代码.

但是,我发现了一种在progressValue发生变化时执行动画的方法.这不是我想要的(仍然没有使用UIView的animateWithDuration:动画:),我不确定这是否是正确的方向.它涉及子类化我的UIView的CALayer.您可以在上面提到的同一个repo中查看源代码,on custom_layer branch.这是direct link到CALayer子类的绘图实现.

我的问题是:拥有一个基于自定义属性(progressValue)绘制一些形状的UIView子类,是否可以使用UIView的animateWithDuration来制作视图动画:动画:更改视图的属性(progressValue)时?

UPDATE

感谢rounak’s answer,我摆脱了drawRect:方法中的自定义形状,并创建了两个单独的CAShapeLayers.每次progressValue更改时,我只是为这些图层设置strokeEnd属性而不是绘制新形状,而是提供与之前相同的视觉效果,但应该表现更好(check out source code).

然后我实现了actionForLayer:forKey:方法(check out source code),它返回CABasicAnimation,它应该在progressValue改变时为strokeEnd设置动画.

但是,我仍然遇到动画问题:

__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];
                                      }];
                 }];

我的循环进度视图为progressValue更改设置动画,并执行上面的代码告诉每个动画大约需要3秒钟.不幸的是,在屏幕上,视图动画非常快(动画在几分之一秒内完成).这是它的样子:

我不确定问题出在哪里,但我注意到将[CABasicAnimation animationWithKeyPath:@“strokeEnd”]中的keypath从strokeEnd更改为其他任何内容都不会产生任何效果(动画总是看起来一样).

任何我在这里缺少的提示将不胜感激.

更新2

我刚刚注意到动画实际上是由setProgressValue中的这两行触发的:setter:

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

(see on GitHub)

看起来我的actionForLayer的实现:forKey:尽管被执行了,但根本不起作用(用brakepoint检查).

我想知道我做错了什么?

更新 – 解决方案

我试图为我的UIView的主图层设置动画,而不是包含椭圆形状的自定义图层.我需要修复actionForLayer:(CALayer *)图层forKey:实现使它工作(check out fixed source code).这解决了我的问题.

最佳答案 我建议您使用CAShapeLayer绘制循环进度,并使用strokeStart,strokeEnd和fillColor属性来实现您的效果.您可能需要两个shapeLayers,一个在另一个之上,但这种方法的优点是shapeLayer的属性可以通过CoreAnimation进行动画处理.

此外,你可以使用像这样的hackery来使动画块中的动画像https://gist.github.com/nicklockwood/d374033b27c62662ac8d一样

点赞