当在animation中调用块时停止填充颜色animation闪烁

我无法弄清楚为什么我的animation块完成后,animation从fromValue闪烁到toValue。 我知道在完成一个animation之后,您必须将CALayer的值设置为animation的结束状态,以保持animation的一致性。 然而,无论我把这些方法称为什么顺序,我总是得到闪烁的结果。 我正在做的是用biezerpath画一个复选标记,然后一旦strokeEndanimation完成,我通过animationfillColor属性填充复选标记。 填充复选标记function和复位复选标记function都是在用户选中与checkmark关联的tableviewcell的行时触发的。 噢,我正在使用AutoLayout,如果这有所作为。

所以我想知道一些事情实际上是一个1)一旦表格视图单元格被显示,我调用公共函数shoudSetCheckmarkToCheckedState设置布尔self.userSelectedCheckmark参数在函数中传递的isChecked。 从那里我调用[self setNeedsLayout],它触发layoutSubviews并调用shouldDrawCheckmark …函数。 我这样做的原因是因为如果我不第一次绘制单元格,就没有框架设置,所以我的绘图看起来很乱。 所以我应该每次我改变userSelectedCheckmark属性或者有更好的方法调用setNeedsLayout。

2)一旦animation完成,为什么复选标记闪烁。 我想我知道为什么,因为当animation完成时,图层属性被重置为与图层开始animation时相同的状态。 那么我该如何解决这个问题? 我只是触发一个计时器来改变animation结束前一毫秒的填充颜色,但是这并不正确。

inheritance人的代码btw

typedef void (^animationCompletionBlock)(void); #define kAnimationCompletionBlock @"animationCompletionBlock" #import "CheckmarkView.h" #import "UIColor+HexString.h" @interface CheckmarkView() @property (nonatomic,strong) CAShapeLayer *checkmarkLayer; @property (nonatomic,assign) BOOL userSelectedCheckmark; - (void)shouldDrawCheckmarkToLayerWithAnimation:(BOOL)animateCheckmark; @end @implementation CheckmarkView #pragma mark - Lifecycle /**********************/ - (id)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { self.translatesAutoresizingMaskIntoConstraints = FALSE; self.layer.cornerRadius = 5.0; [self setClipsToBounds:TRUE]; } return self; } - (void)layoutSubviews{ [self shouldDrawCheckmarkToLayerWithAnimation:self.userSelectedCheckmark]; } #pragma mark - Public Methods /***************************/ - (void)shouldSetCheckmarkToCheckedState:(BOOL)isChecked{ self.userSelectedCheckmark = isChecked; [self setNeedsLayout]; } #pragma mark - Private Methods /****************************/ - (void)shouldDrawCheckmarkToLayerWithAnimation:(BOOL)animateCheckmark{ if(self.userSelectedCheckmark){ CGRect superlayerRect = self.bounds; if(!self.checkmarkLayer){ self.checkmarkLayer = [CAShapeLayer layer]; [self.checkmarkLayer setStrokeColor:[UIColor whiteColor].CGColor]; UIBezierPath *checkMarkPath = [UIBezierPath bezierPath]; //Start Point [checkMarkPath moveToPoint:CGPointMake(CGRectGetMinX(superlayerRect) + 5, CGRectGetMinY(superlayerRect) + 14)]; //Bottom Point [checkMarkPath addLineToPoint:CGPointMake(CGRectGetMidX(superlayerRect), CGRectGetMaxY(superlayerRect) - 4)]; //Top Right of self.checkmarkLayer [checkMarkPath addLineToPoint:CGPointMake(CGRectGetMaxX(superlayerRect) - 5, CGRectGetMinY(superlayerRect) + 8)]; [checkMarkPath addLineToPoint:CGPointMake(checkMarkPath.currentPoint.x - 3, checkMarkPath.currentPoint.y - 4)]; //Top Middle Point [checkMarkPath addLineToPoint:CGPointMake(CGRectGetMidX(superlayerRect) - 1, CGRectGetMidY(superlayerRect) + 2)]; //Top left of self.checkmarkLayer [checkMarkPath addLineToPoint:CGPointMake(CGRectGetMinX(superlayerRect) + 7, CGRectGetMinY(superlayerRect) + 10)]; [checkMarkPath closePath]; [self.checkmarkLayer setPath:checkMarkPath.CGPath]; } self.layer.backgroundColor = [UIColor colorWithHexString:UIColorOrangeB0].CGColor; [self.checkmarkLayer setFillColor:[UIColor colorWithHexString:UIColorOrangeB0].CGColor]; [self.layer addSublayer:self.checkmarkLayer]; if(animateCheckmark){ animationCompletionBlock block; block = ^(void){ [self.checkmarkLayer setFillColor:[UIColor whiteColor].CGColor]; }; CABasicAnimation *strokeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; [strokeAnimation setBeginTime:0.0]; [strokeAnimation setFromValue:@(0.0f)]; [strokeAnimation setToValue:@(1.0f)]; [strokeAnimation setDuration:.8]; CABasicAnimation *fillAnimation = [CABasicAnimation animationWithKeyPath:@"fillColor"]; [fillAnimation setBeginTime:strokeAnimation.duration + .2]; [fillAnimation setDuration:.2]; [fillAnimation setFromValue:(id)[UIColor colorWithHexString:UIColorOrangeB0].CGColor]; [fillAnimation setToValue:(id)[UIColor whiteColor].CGColor]; CAAnimationGroup *group = [CAAnimationGroup animation]; group.delegate = self; [group setDuration:1.5]; [group setAnimations:@[strokeAnimation,fillAnimation]]; [group setValue:block forKey:kAnimationCompletionBlock]; [self.checkmarkLayer addAnimation:group forKey:nil]; } } else{ self.layer.backgroundColor = [UIColor colorWithHexString:UIColorWhiteOffset].CGColor; [self.checkmarkLayer setFillColor:[UIColor colorWithHexString:UIColorOrangeB0].CGColor]; [self.checkmarkLayer removeFromSuperlayer]; } } #pragma mark - CAAnimationBlock - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock]; if (theBlock) theBlock(); } 

回答你的第一个问题

layoutSubviews只会在每个运行循环中调用一次。 您可以尽可能多地调用[self setNeedsLayout],而无需担心无需布置视图。

参考:Kubicek,吉姆。 “滥用UIView” 2012年10月11日。http://jimkubicek.com/blog/2012/10/11/using-uiview/

要回答你的第二个问题,你说的是为什么闪烁。 问题是无法保证animationDidStopcallback方法将在图层外观重置之前调用。

有几种方法可以解决这个问题,下面的方法只需要添加一些额外的代码而不需要改变现有的代码。

 //kCAFillModeForwards: the animatable properties take on the end value once it has finished [strokeAnimation setFillMode:kCAFillModeForwards]; [strokeAnimation setRemovedOnCompletion:NO]; [fillAnimation setFillMode:kCAFillModeForwards]; [fillAnimation setRemovedOnCompletion:NO]; [group setFillMode:kCAFillModeForwards]; [group setRemovedOnCompletion:NO]; [self.checkmarkLayer addAnimation:group forKey:nil]; 

当一个CAAnimation完成时,它将从图层中移除并导致图层重置,所以我们要做的第一件事就是停止移除animation。

 - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock]; if (theBlock) theBlock(); [self.checkmarkLayer removeAllAnimations]; 

}

当animationDidStop方法被调用时,我们像之前一样设置图层的属性,然后我们从图层中删除animation。

还有一件事要考虑的是,当你改变一个CALayer的外观时,它会隐式(自动)animation。 所以当你设置完成块时,你要显式地告诉核心animation不要animation

 animationCompletionBlock block; block = ^(void){ [CATransaction begin]; [CATransaction setDisableActions:YES]; [self.checkmarkLayer setFillColor:[UIColor whiteColor].CGColor]; [CATransaction commit]; };