CALayer 上的隐式动画没有动画
Implicit Animation on CALayer not animating
我正在制作一个我想要制作动画的圆形进度条。一切都按预期绘制并且有效,但是当我尝试制作动画时,我立即得到了变化而不是动画。任何帮助将不胜感激。
这是我的图层:
@interface CircularProgressLayer : CALayer
@property (nonatomic, assign) CGFloat progressDegrees;
@property (nonatomic, assign) CGFloat trackWidth;
@end
@implementation CircularProgressLayer : CALayer
@dynamic progressDegrees;
@dynamic trackWidth;
- (id)initWithLayer:(id)layer
{
if ((self = [super initWithLayer:layer]))
{
if ([layer isKindOfClass:[CircularProgressLayer class]])
{
self.progressDegrees = ((CircularProgressLayer*)layer).progressDegrees;
}
}
return self;
}
// Instruct to Core Animation that a change in the custom property should automatically trigger a redraw of the layer.
+ (BOOL)needsDisplayForKey:(NSString*)key
{
if ([key isEqualToString:@"progressDegrees"])
{
return YES;
}
if ([key isEqualToString:@"trackWidth"])
{
return YES;
}
return [super needsDisplayForKey:key];
}
// Needed to support implicit animation of this property.
// Return the basic animation the implicit animation will leverage.
- (id<CAAction>)actionForKey:(NSString *)key
{
if ([key isEqualToString:@"progressDegrees"])
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
animation.fromValue = [[self presentationLayer] valueForKey:key];
animation.duration = 5.0;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
return animation;
}
return [super actionForKey:key];
}
据我了解,在 actionForKey:
中返回动画应该是使动画正常工作所需的全部内容,但我什么也没得到。我的所有绘图当前都包含在实现此层的视图的 drawLayer:inContext:
方法中(因为它起源于 drawRect 代码,然后我才知道我必须将它放在一个层中以使其具有动画效果,而且我还没有' t converted everything over), 但是 sample code 我从 Apple 下载的使它看起来很好。
下面是绘图代码:
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
[self drawTrackInContext:context];
[self drawProgressInContext:context];
UIGraphicsPopContext();
}
- (void)drawProgressInContext:(CGContextRef)context
{
if (self.progress < 1)
{
// No progress, get out
return;
}
CGSize size = self.bounds.size;
FlipContextVertically(size);
CGContextSaveGState(context);
{
UIBezierPath *circle = [self circle];
CGContextSetLineWidth(context, 0.0);
CGFloat degrees = 360.0;
for (int i = 0; i < degrees; i++)
{
if (i > ((RMONCircularProgressLayer *)self.layer).progressDegrees)
{
break;
}
CGFloat theta = 2 * M_PI * (CGFloat) i / degrees;
CGFloat x = self.radius * sin(theta);
CGFloat y = self.radius * cos(theta);
MovePathCenterToPoint(circle, CGPointMake(x, y));
OffsetPath(circle, CGSizeMake(size.width / 2, size.height / 2));
CGContextSetFillColorWithColor(context, [self colorForDegree:i].CGColor);
CGContextAddPath(context, circle.CGPath);
CGContextDrawPath(context, kCGPathFillStroke);
}
}
CGContextRestoreGState(context);
}
- (void)drawTrackInContext:(CGContextRef)context
{
CGContextSaveGState(context);
{
// Draw the circle covering middle of the screen
CGSize size = self.bounds.size;
CGContextSetLineWidth(context, self.trackWidth); // will only be filled color
CGContextSetStrokeColorWithColor(context, self.trackColor.CGColor);
CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
CGContextSetAlpha(context, 1.0);
CGContextAddArc(context, (CGFloat)size.width/2.0, (CGFloat)size.height/2.0, self.radius, 0, (CGFloat)M_PI*2.0, 1);
CGContextDrawPath(context, kCGPathFillStroke);
}
CGContextRestoreGState(context);
}
drawTrackInContext
只是画了一个圆圈,里面会包含进度圈。 drawProgressInContext
是我希望制作动画的绘图代码。 iOS Erica Sadun 的绘图中有一些辅助函数,它们执行一些我没有包括的上下文和路径操作,函数名称应该是相当自我解释的。
如有任何帮助,我们将不胜感激。
仅供参考:如果更容易的话,我非常愿意将其制作成明确的动画,但我最终朝这个方向前进,因为我也无法做到这一点。
当图层 属性 被动画化时,属性 的值不会随时间变化以表示中间值。相反,层时间中给定时刻的 属性 的值是从活动动画派生的,并在层的 presentationLayer
.
中可用。
当动画处于活动状态时,drawInContext:
在表示层而不是主层上调用。同样,-drawLayer:inContext:
接收表示层而不是主层。
您遇到的问题是 -drawProgressInContext:
不使用调用函数中的层,而是读取 self.layer.progressDegrees
以获取要绘制的值,该值将始终是分配的值.解决方法是将图层传递给该函数并读取值:
- (void)drawProgressInLayer:(CALayer *)layer context:(CGContextRef)context
{
...
if (i > ((RMONCircularProgressLayer *)layer).progressDegrees) {
...
您在 if (self.progress < 1)
中止时也遇到了问题。这将使它在从非零值变为零时无法设置动画。同样的修复将适用:if ((RMONCircularProgressLayer *)layer).progressDegrees < 1)
我正在制作一个我想要制作动画的圆形进度条。一切都按预期绘制并且有效,但是当我尝试制作动画时,我立即得到了变化而不是动画。任何帮助将不胜感激。
这是我的图层:
@interface CircularProgressLayer : CALayer
@property (nonatomic, assign) CGFloat progressDegrees;
@property (nonatomic, assign) CGFloat trackWidth;
@end
@implementation CircularProgressLayer : CALayer
@dynamic progressDegrees;
@dynamic trackWidth;
- (id)initWithLayer:(id)layer
{
if ((self = [super initWithLayer:layer]))
{
if ([layer isKindOfClass:[CircularProgressLayer class]])
{
self.progressDegrees = ((CircularProgressLayer*)layer).progressDegrees;
}
}
return self;
}
// Instruct to Core Animation that a change in the custom property should automatically trigger a redraw of the layer.
+ (BOOL)needsDisplayForKey:(NSString*)key
{
if ([key isEqualToString:@"progressDegrees"])
{
return YES;
}
if ([key isEqualToString:@"trackWidth"])
{
return YES;
}
return [super needsDisplayForKey:key];
}
// Needed to support implicit animation of this property.
// Return the basic animation the implicit animation will leverage.
- (id<CAAction>)actionForKey:(NSString *)key
{
if ([key isEqualToString:@"progressDegrees"])
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];
animation.fromValue = [[self presentationLayer] valueForKey:key];
animation.duration = 5.0;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
return animation;
}
return [super actionForKey:key];
}
据我了解,在 actionForKey:
中返回动画应该是使动画正常工作所需的全部内容,但我什么也没得到。我的所有绘图当前都包含在实现此层的视图的 drawLayer:inContext:
方法中(因为它起源于 drawRect 代码,然后我才知道我必须将它放在一个层中以使其具有动画效果,而且我还没有' t converted everything over), 但是 sample code 我从 Apple 下载的使它看起来很好。
下面是绘图代码:
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
[self drawTrackInContext:context];
[self drawProgressInContext:context];
UIGraphicsPopContext();
}
- (void)drawProgressInContext:(CGContextRef)context
{
if (self.progress < 1)
{
// No progress, get out
return;
}
CGSize size = self.bounds.size;
FlipContextVertically(size);
CGContextSaveGState(context);
{
UIBezierPath *circle = [self circle];
CGContextSetLineWidth(context, 0.0);
CGFloat degrees = 360.0;
for (int i = 0; i < degrees; i++)
{
if (i > ((RMONCircularProgressLayer *)self.layer).progressDegrees)
{
break;
}
CGFloat theta = 2 * M_PI * (CGFloat) i / degrees;
CGFloat x = self.radius * sin(theta);
CGFloat y = self.radius * cos(theta);
MovePathCenterToPoint(circle, CGPointMake(x, y));
OffsetPath(circle, CGSizeMake(size.width / 2, size.height / 2));
CGContextSetFillColorWithColor(context, [self colorForDegree:i].CGColor);
CGContextAddPath(context, circle.CGPath);
CGContextDrawPath(context, kCGPathFillStroke);
}
}
CGContextRestoreGState(context);
}
- (void)drawTrackInContext:(CGContextRef)context
{
CGContextSaveGState(context);
{
// Draw the circle covering middle of the screen
CGSize size = self.bounds.size;
CGContextSetLineWidth(context, self.trackWidth); // will only be filled color
CGContextSetStrokeColorWithColor(context, self.trackColor.CGColor);
CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
CGContextSetAlpha(context, 1.0);
CGContextAddArc(context, (CGFloat)size.width/2.0, (CGFloat)size.height/2.0, self.radius, 0, (CGFloat)M_PI*2.0, 1);
CGContextDrawPath(context, kCGPathFillStroke);
}
CGContextRestoreGState(context);
}
drawTrackInContext
只是画了一个圆圈,里面会包含进度圈。 drawProgressInContext
是我希望制作动画的绘图代码。 iOS Erica Sadun 的绘图中有一些辅助函数,它们执行一些我没有包括的上下文和路径操作,函数名称应该是相当自我解释的。
如有任何帮助,我们将不胜感激。
仅供参考:如果更容易的话,我非常愿意将其制作成明确的动画,但我最终朝这个方向前进,因为我也无法做到这一点。
当图层 属性 被动画化时,属性 的值不会随时间变化以表示中间值。相反,层时间中给定时刻的 属性 的值是从活动动画派生的,并在层的 presentationLayer
.
当动画处于活动状态时,drawInContext:
在表示层而不是主层上调用。同样,-drawLayer:inContext:
接收表示层而不是主层。
您遇到的问题是 -drawProgressInContext:
不使用调用函数中的层,而是读取 self.layer.progressDegrees
以获取要绘制的值,该值将始终是分配的值.解决方法是将图层传递给该函数并读取值:
- (void)drawProgressInLayer:(CALayer *)layer context:(CGContextRef)context
{
...
if (i > ((RMONCircularProgressLayer *)layer).progressDegrees) {
...
您在 if (self.progress < 1)
中止时也遇到了问题。这将使它在从非零值变为零时无法设置动画。同样的修复将适用:if ((RMONCircularProgressLayer *)layer).progressDegrees < 1)