Im having trouble figuring out why the animation is flickering from the fromValue to the toValue after my animation block is complete. I know that after you complete an animation you have to set the values of the CALayer to the end state of the animation to keep it looking consistent. Yet no matter what order i call these methods in i keep getting the flickering result. What I'm doing is drawing a checkmark with a biezer path and then once the strokeEnd animation is complete i fill in the check mark by animating the fillColor property. The fill in checkmark function and reset checkmark function are all triggered when the user selects the row of the tableviewcell the checkmark is associated with. Oh ya and I am using AutoLayout if that makes a difference.
So I'm wondering a few things actually one 1) Once the table view cell is being displayed i call the public function shoudSetCheckmarkToCheckedState which sets the boolean self.userSelectedCheckmark to the parameter isChecked thats passed in the function. From there i call [self setNeedsLayout],which triggers layoutSubviews and it calls the shouldDrawCheckmark... function. The reason why I do this is because if I don't the first time the cell is drawn there is no frame set so my drawing looks a mess. So should I be calling setNeedsLayout every time i change the userSelectedCheckmark property or is there a better way.
2) Why is the checkmark flickering once the animation is complete. I think i know why, its because when the animation completes the layers properties are reset to the same state they were in when the layer started animating. So then how can I fix this? I would just trigger a timer to change the fill color a millisecond before the animation ends but that doesn't feel right.
heres the code 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();
}