使用CABasicAnimation对整个圆进行连续不断的动画绘制,而在重新启动过程中没有任何滞后 [英] Animate drawing of full circle continuously without any lag during restart using CABasicAnimation

查看:90
本文介绍了使用CABasicAnimation对整个圆进行连续不断的动画绘制,而在重新启动过程中没有任何滞后的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在视图控制器中制作动画,其中圆随着动画旋转。圆应该旋转直到完成以下gif所示的过程。我已经实现了圆形动画,但无法达到想要达到的目的。

 导入UIKit 

class ViewController:UIViewController {

var circle:Circle ?;

覆盖func viewDidLoad(){
super.viewDidLoad();

view.backgroundColor = UIColor.white;

setupViews();
}

func setupViews(){
circle = Circle(frame:self.view.frame);

view.addSubview(circle!);

个圆?.leftAnchor.constraint(equalTo:view.leftAnchor).isActive = true;
circle?.topAnchor.constraint(equalTo:view.topAnchor).isActive = true;
circle?.heightAnchor.constraint(equalTo:view.heightAnchor).isActive = true;
circle?.widthAnchor.constraint(equalTo:view.widthAnchor).isActive = true;

}

}

class Circle:UIView {


覆盖init(框架:CGRect) {
super.init(frame:frame);

self.backgroundColor = .blue;
self.translatesAutoresizingMaskIntoConstraints = false;

setupCircle();
}

是否需要init?(编码器aDecoder:NSCoder){
fatalError( init(coder :)未实现)
}
让gradientLayer = CAGradientLayer();


func setupCircle(){

layer.addSublayer(shapeLayer);

let circlePath = UIBezierPath(arcCenter:CGPoint(x:self.frame.width / 2-50,y:self.frame.height / 2-50),radius:50,startAngle:CGFloat( Double.pi *(0/4)),endAngle:CGFloat(Double.pi * 2),顺时针:true);

shapeLayer.path = circlePath.cgPath;

让组= CAAnimationGroup()
group.animations = [animateStrokeEnd,animateOpacity]
group.duration = 0.8

group.repeatCount = HUGE / /重复永远
shapeLayer.add(group,forKey:nil)
}

let shapeLayer:CAShapeLayer = {
let layer = CAShapeLayer();
layer.strokeColor = UIColor.white.cgColor;
layer.lineWidth = 5;
layer.fillColor = UIColor.clear.cgColor;
layer.strokeStart = 0
layer.strokeEnd = 1;
返回层;
}();

let animateOpacity:CABasicAnimation = {
let animation = CABasicAnimation(keyPath: opacity);
animation.fromValue = 0;
animation.toValue = 0.8;
animation.byValue = 0.01;
animation.repeatCount = Float.infinity;
返回动画
}();

let animateStrokeEnd:CABasicAnimation = {
let animation = CABasicAnimation(keyPath: strokeEnd);
animation.fromValue = 0;
animation.repeatCount = Float.infinity;
animation.toValue = 1;
个返回动画;
}();

}

我正在使用strokeEnd动画实现该动画。并为颜色设置不透明度。但是,当圆达到360度时,它会滞后于开始新的圆。



有人知道如何消除这种效果并获得流畅的动画吗?




I am trying to have an animation in a view controller in which the circle rotates with animation. The circle should rotate until a process completed like a gif below. I have implemented the circle animation but couldn't reach to the point what I want to achieve.

import UIKit

class ViewController: UIViewController {

    var circle : Circle?;

    override func viewDidLoad() {
        super.viewDidLoad();

        view.backgroundColor = UIColor.white;

        setupViews();
    }

    func setupViews(){
        circle = Circle(frame: self.view.frame);

        view.addSubview(circle!);

        circle?.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true;
        circle?.topAnchor.constraint(equalTo: view.topAnchor).isActive = true;
        circle?.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true;
        circle?.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true;

    }

}

class Circle : UIView{


    override init(frame: CGRect) {
        super.init(frame: frame);

        self.backgroundColor = .blue;
        self.translatesAutoresizingMaskIntoConstraints = false;

        setupCircle();
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    let gradientLayer = CAGradientLayer();


    func setupCircle(){

        layer.addSublayer(shapeLayer);

        let circlePath = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2 - 50, y: self.frame.height / 2 - 50), radius: 50, startAngle: CGFloat(Double.pi * (0 / 4)), endAngle: CGFloat(Double.pi * 2), clockwise: true);

        shapeLayer.path = circlePath.cgPath;

        let group = CAAnimationGroup()
        group.animations = [animateStrokeEnd, animateOpacity]
        group.duration = 0.8

        group.repeatCount = HUGE // repeat forver
        shapeLayer.add(group, forKey: nil)
    }

    let shapeLayer: CAShapeLayer = {
        let layer = CAShapeLayer();
        layer.strokeColor = UIColor.white.cgColor;
        layer.lineWidth = 5;
        layer.fillColor = UIColor.clear.cgColor;
        layer.strokeStart = 0
        layer.strokeEnd = 1;
        return layer;
    }();

    let animateOpacity : CABasicAnimation = {
        let animation = CABasicAnimation(keyPath: "opacity");
        animation.fromValue = 0;
        animation.toValue = 0.8;
        animation.byValue = 0.01;
        animation.repeatCount = Float.infinity;
        return animation
    }();

    let animateStrokeEnd: CABasicAnimation = {
        let animation = CABasicAnimation(keyPath: "strokeEnd");
        animation.fromValue  = 0;
        animation.repeatCount = Float.infinity;
        animation.toValue = 1;
        return animation;
    }();

}

I am using strokeEnd animation to implement the animation. And opacity to animate the color. But when the circle reaches 360 degree, its makes a lag before starting a new circle.

Does anybody know how to remove this effect and get smooth animation?

The above code produces this animation

But i want to achieve this animation

Also the stroke colour is different from the original animation. Can we achieve this animation using the CABasicAnimation?

解决方案

Rather than trying to animate the actual drawing, just draw the view once and then animate it.

Here is a custom PadlockView and a custom CircleView which mimic the animation you showed. To use it, add the code below to your project. Add a UIView to your Storyboard, change its class to PadlockView, and make an @IBOutlet to it (called padlock perhaps). When you want the view to animate, set padlock.circle.isAnimating = true. To stop animating, set padlock.circle.isAnimating = false.


CircleView.swift

// This UIView extension was borrowed from @keval's answer:
// https://stackoverflow.com/a/41160100/1630618
extension UIView {
    func rotate360Degrees(duration: CFTimeInterval = 3) {
        let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
        rotateAnimation.fromValue = 0.0
        rotateAnimation.toValue = CGFloat.pi * 2
        rotateAnimation.isRemovedOnCompletion = false
        rotateAnimation.duration = duration
        rotateAnimation.repeatCount = Float.infinity
        self.layer.add(rotateAnimation, forKey: nil)
    }
}

class CircleView: UIView {

    var foregroundColor = UIColor.white
    var lineWidth: CGFloat = 3.0

    var isAnimating = false {
        didSet {
            if isAnimating {
                self.isHidden = false
                self.rotate360Degrees(duration: 1.0)
            } else {
                self.isHidden = true
                self.layer.removeAllAnimations()
            }
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {
        self.isHidden = true
        self.backgroundColor = .clear
    }

    override func draw(_ rect: CGRect) {
        let width = bounds.width
        let height = bounds.height
        let radius = (min(width, height) - lineWidth) / 2.0

        var currentPoint = CGPoint(x: width / 2.0 + radius, y: height / 2.0)
        var priorAngle = CGFloat(360)

        for angle in stride(from: CGFloat(360), through: 0, by: -2) {
            let path = UIBezierPath()
            path.lineWidth = lineWidth

            path.move(to: currentPoint)
            currentPoint = CGPoint(x: width / 2.0 + cos(angle * .pi / 180.0) * radius, y: height / 2.0 + sin(angle * .pi / 180.0) * radius)
            path.addArc(withCenter: CGPoint(x: width / 2.0, y: height / 2.0), radius: radius, startAngle: priorAngle * .pi / 180.0 , endAngle: angle * .pi / 180.0, clockwise: false)
            priorAngle = angle

            foregroundColor.withAlphaComponent(angle/360.0).setStroke()
            path.stroke()
        }
    }

}

PadlockView.swift

class PadlockView: UIView {

    var circle: CircleView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {
        self.backgroundColor = .clear

        circle = CircleView()
        circle.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(circle)
        circle.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
        circle.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
        circle.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
        circle.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
    }

    override func draw(_ rect: CGRect) {
        let width = bounds.width
        let height = bounds.height

        let lockwidth = width / 3
        let lockheight = height / 4

        let boltwidth = lockwidth * 2 / 3

        UIColor.white.setStroke()

        let path = UIBezierPath()
        path.move(to: CGPoint(x: (width - lockwidth) / 2, y: height / 2))
        path.addLine(to: CGPoint(x: (width + lockwidth) / 2, y: height / 2))
        path.addLine(to: CGPoint(x: (width + lockwidth) / 2, y: height / 2 + lockheight))
        path.addLine(to: CGPoint(x: (width - lockwidth) / 2, y: height / 2 + lockheight))
        path.close()
        path.move(to: CGPoint(x: (width - boltwidth) / 2, y: height / 2))
        path.addLine(to: CGPoint(x: (width - boltwidth) / 2, y: height / 2 - boltwidth / 4))
        path.addArc(withCenter: CGPoint(x: width/2, y: height / 2 - boltwidth / 4), radius: boltwidth / 2, startAngle: .pi, endAngle: 0, clockwise: true)
        path.lineWidth = 2.0
        path.stroke()
    }

}

Note: Continuous animation code courtesy of this answer.


Here is a demo that I setup with the following code in my ViewController:

@IBOutlet weak var padlock: PadlockView!

@IBAction func startStop(_ sender: UIButton) {
    if sender.currentTitle == "Start" {
        sender.setTitle("Stop", for: .normal)
        padlock.circle.isAnimating = true
    } else {
        sender.setTitle("Start", for: .normal)
        padlock.circle.isAnimating = false
    }
}

这篇关于使用CABasicAnimation对整个圆进行连续不断的动画绘制,而在重新启动过程中没有任何滞后的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆