I am trying to make a simple check mark control, which consists of just a square and a checkmark in it. I would like to animate drawing and erasing of the check mark when the control is tapped. This is what I have so far:
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: checkBox.size * 2 / 3))
path.addLine(to: CGPoint(x: checkBox.size / 3, y: checkBox.size))
path.addLine(to: CGPoint(x: checkBox.size, y: 0))
pathLayer.frame = checkBox.bounds
pathLayer.path = path.cgPath
pathLayer.strokeColor = UIColor.red().cgColor
pathLayer.fillColor = nil
pathLayer.lineWidth = checkMarkWidth
pathLayer.lineJoin = kCALineJoinBevel
let pathAnimation = CABasicAnimation(keyPath:"strokeEnd")
pathAnimation.duration = checkMarkAnimationDuration
pathAnimation.fromValue = NSNumber(floatLiteral: 0)
pathAnimation.toValue = NSNumber(floatLiteral: 1)
let reverseAnimation = CABasicAnimation(keyPath:"strokeEnd")
reverseAnimation.duration = checkMarkAnimationDuration
reverseAnimation.fromValue = NSNumber(floatLiteral: 1)
reverseAnimation.toValue = NSNumber(floatLiteral: 0)
reverseAnimation.delegate = self
Then I start the animations like this:
var on: Bool = false {
didSet {
if on {
checkBox.layer.addSublayer(pathLayer)
pathLayer.removeAllAnimations()
pathLayer.add(pathAnimation, forKey:"strokeEnd")
} else {
pathLayer.removeAllAnimations()
pathLayer.add(reverseAnimation, forKey:"strokeEnd")
}
}
}
Finally in my CAAnimationDelegate I do this:
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
pathLayer.removeFromSuperlayer()
}
This looks good, except that when I test it on the real device there is a little flash when the reversed animation is done, but before the layer is removed. It seems I am missing an animationWillStop
callback.
Any ideas how to fix this glitch?
Also any other suggestions on how to improve this code are welcome, cause I'm completely new to Core Animation.
Core Animation has three layer tree, one is model layer tree , which you mostly interact with. one is presentation layer tree , which contains in-flight running animation values. and the last one is render layer tree , which actually render layer and private to Core Animation.
CAAnimation Only change value on CALayer's presentationLayer. after animation end, the animation is removed and strokeEnd jump back to your modal layer's value: 1.0. This is why you have flash.
so, you can change
var on: Bool = false {
didSet {
if on {
checkBox.layer.addSublayer(pathLayer)
pathLayer.removeAllAnimations()
pathLayer.add(pathAnimation, forKey:"strokeEnd")
} else {
pathLayer.removeAllAnimations()
pathLayer.add(reverseAnimation, forKey:"strokeEnd")
}
}
}
to
var on: Bool = false {
didSet {
if on {
checkBox.layer.addSublayer(pathLayer)
pathLayer.strokeEnd = 1.0 // set property to final state
pathLayer.removeAllAnimations()
pathLayer.add(pathAnimation, forKey:"strokeEnd")
} else {
pathLayer.strokeEnd = 0.0 // set property to final state
pathLayer.removeAllAnimations()
pathLayer.add(reverseAnimation, forKey:"strokeEnd")
}
}
}
You can implement these steps:
Step1:
Step2:
#import < QuartzCore/QuartzCore.h>
#define RectWidth(f) f.size.width
#define RectHeight(f) f.size.height
- (void)doMaskAnimationOnCheckMarck:(UIImageView*)imgView duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay alpha:(CGFloat)alpha{
UIBezierPath * fromPath = [UIBezierPath bezierPathWithRect:CGRectMake(0,0, 1,RectHeight(imgView.bounds))];
UIBezierPath * toPath = [UIBezierPath bezierPathWithRect:imgView.bounds];
CAShapeLayer * maaskLayer = [CAShapeLayer layer];
maaskLayer.path = toPath.CGPath;
maaskLayer.fillColor = [UIColor colorWithWhite:1.0 alpha:alpha].CGColor;
imgView.layer.mask = maskLayer;
CABasicAnimation * animation = [CABasicAnimation animation]; animation.keyPath = @"path";
animation.beginTime = CACurrentMediaTime() + delay;
animation.fromValue = (__bridge id)fromPath.CGPath; animation.toValue = (__bridge id)toPath.CGPath;
animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[maaskLayer addAnimation:animation forKey:@"MaskAnimation"];
}
You should have better luck using this link as follows.
How to animate a checkmark Using Bezier path as Clipping Mask in iOS
Hope it will help.
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.