CAAnimation 为复选标记绘图设置动画

CAAnimation to animate a checkmark drawing

我正在尝试制作一个简单的复选标记控件,其中仅包含一个正方形和一个复选标记。我想在点击控件时为复选标记的绘制和擦除设置动画。这是我目前所拥有的:

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

然后我开始这样的动画:

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")
        }
    }
}

最后在我的 CAAnimationDelegate 中我这样做了:

public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
    pathLayer.removeFromSuperlayer()
}

这看起来不错,除了当我在真实设备上测试它时,当反转动画完成时,但在图层被移除之前,有一点闪光。看来我缺少一个 animationWillStop 回调。

有什么办法解决这个问题吗?

也欢迎任何其他关于如何改进此代码的建议,因为我是 Core Animation 的新手。

Core Animation 有三层树,一层是 模型层树,您最常与之交互。一个是 表示层树 ,其中包含飞行中的 运行 动画值。最后一个是 render layer tree,它实际上是渲染层并且是 Core Animation 私有的。

CAAnimation 只改变 CALayer 的 presentationLayer 的值。动画结束后,动画被移除,strokeEnd 跳回 到你的模态层的值:1.0。这就是为什么你有闪光灯。

所以,你可以改变

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")
        }
    }
}

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")
        }
    }
}

您可以执行以下步骤:

第一步:

  • 创建一个名为 CheckMarkAnimation 的 xcode 项目。 在您的界面构建器中添加一个带有图像“checkmark.png”的 UIImageView。

第二步:

  • 我刚刚编写了下面的代码片段。

#import

#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"];
}

你使用这个 link 应该会有更好的运气,如下所示。

How to animate a checkmark Using Bezier path as Clipping Mask in iOS

希望对您有所帮助。