简体   繁体   中英

CAAnimation to animate a checkmark drawing

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:

  • Create an xcode project with name CheckMarkAnimation . In your interface builder add an UIImageView with image " checkmark.png ".

Step2:

  • I have just written the code snippets below.

#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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM