Core Animation - modify animation property

后端 未结 3 913
没有蜡笔的小新
没有蜡笔的小新 2020-12-21 13:47

I have animation

 func startRotate360() {
    let rotation : CABasicAnimation = CABasicAnimation(keyPath: \"transform.rotation.z\")
    rotation.fromValue =          


        
相关标签:
3条回答
  • 2020-12-21 14:04

    Maybe this is not the best solution but it works, as you say you can not modify properties of the CABasicAnimation once is created, also we need to remove the rotation.repeatCount = Float.greatestFiniteMagnitude, if notCAAnimationDelegatemethodanimationDidStop` is never called, with this approach the animation can be stoped without any problems as you need

    step 1: first declare a variable flag to mark as you need stop animation in your custom class

    var needStop : Bool = false
    

    step 2: add a method to stopAnimation after ends

    func stopAnimation()
    {
        self.needStop = true
    }
    

    step 3: add a method to get your custom animation

    func getRotate360Animation() ->CAAnimation{
        let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotation.fromValue = 0
        rotation.toValue =  Double.pi * 2
        rotation.duration = 1
        rotation.isCumulative = true
        rotation.isRemovedOnCompletion = false
        return rotation
    }
    

    step 4: Modify your startRotate360 func to use your getRotate360Animation() method

    func startRotate360() {
        let rotationAnimation = self.getRotate360Animation()
        rotationAnimation.delegate = self
        self.layer.add(rotationAnimation, forKey: "rotationAnimation")
    }
    

    step 5: Implement CAAnimationDelegate in your class

    extension YOURCLASS : CAAnimationDelegate
    {
    
        func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
            if(anim == self.layer?.animation(forKey: "rotationAnimation"))
            {
                self.layer?.removeAnimation(forKey: "rotationAnimation")
                if(!self.needStop){
                    let animation = self.getRotate360Animation()
                    animation.delegate = self
                    self.layer?.add(animation, forKey: "rotationAnimation")
                }
            }
        }
    }
    

    This works and was tested

    Hope this helps you

    0 讨论(0)
  • 2020-12-21 14:23

    2019 typical modern syntax

    Setup the arc and the layer like this:

    import Foundation
    import UIKit
    
    class RoundChaser: UIView {
        
        private let lineThick: CGFloat = 10.0
        private let beginFraction: CGFloat = 0.15
        // where does the arc drawing begin?
        // 0==top, .25==right, .5==bottom, .75==left
        
        private lazy var arcPath: CGPath = {
            let b = beginFraction * .pi * 2.0
            return UIBezierPath(
                arcCenter: bounds.centerOfCGRect(),
                radius: bounds.width / 2.0 - lineThick / 2.0,
                startAngle: .pi * -0.5 + b,
                // recall that .pi * -0.5 is the "top"
                endAngle: .pi * 1.5 + b,
                clockwise: true
            ).cgPath
        }()
        
        private lazy var arcLayer: CAShapeLayer = {
            let l = CAShapeLayer()
            l.path = arcPath
            l.fillColor = UIColor.clear.cgColor
            l.strokeColor = UIColor.purple.cgColor
            l.lineWidth = lineThick
            l.lineCap = CAShapeLayerLineCap.round
            l.strokeStart = 0
            l.strokeEnd = 0
            // if both are same, it is hidden. initially hidden
            layer.addSublayer(l)
            return l
        }()
        
    

    then initialization is this easy

        open override func layoutSubviews() {
            super.layoutSubviews()
            arcLayer.frame = bounds
        }
        
    

    finally animation is easy

        public func begin() {
            CATransaction.begin()
            
            let e : CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd")
            e.duration = 2.0
            e.fromValue = 0
            e.toValue = 1.0
            // recall 0 will be our beginFraction, see above
            e.repeatCount = .greatestFiniteMagnitude
            
            self.arcLayer.add(e, forKey: nil)
            CATransaction.commit()
        }
    }
    
    0 讨论(0)
  • 2020-12-21 14:27

    Give this a go. You can in fact change CAAnimations that are in progress. There are so many ways. This is the fastest/simplest. You could even stop the animation completely and resume it without the user even noticing.

    You can see the start animation function along with the stop. The start animation looks similar to yours while the stop grabs the current rotation from the presentation layer and creates an animation to rotate until complete. I also smoothed out the duration to be a percentage of the time needed to complete based on current rotation z to full rotation based on the running animation. Then I remove the animation with the repeat count and add the new animation. You can see the view rotate smoothly to the final position and stop. You will get the idea. Drop it in and run it and see what you think. Hit the button to start and hit it again to see it finish rotation and stop.

    import UIKit
    
    class ViewController: UIViewController {
    
        var animationView = UIView()
        var button = UIButton()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            animationView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
            animationView.backgroundColor = .green
            animationView.center = view.center
            self.view.addSubview(animationView)
    
            let label = UILabel(frame: animationView.bounds)
            label.text = "I Spin"
            animationView.addSubview(label)
    
    
            button = UIButton(frame: CGRect(x: 20, y: animationView.frame.maxY + 60, width: view.bounds.width - 40, height: 40))
            button.setTitle("Animate", for: .normal)
            button.setTitleColor(.blue, for: .normal)
            button.addTarget(self, action: #selector(ViewController.pressed), for: .touchUpInside)
            self.view.addSubview(button)
    
        }
    
        func pressed(){
    
            if let title = button.titleLabel?.text{
                let trans = CATransition()
                trans.type = "rippleEffect"
                trans.duration = 0.6
                button.layer.add(trans, forKey: nil)
    
                switch title {
                case "Animate":
                    //perform animation
                    button.setTitle("Stop And Finish", for: .normal)
                    rotateAnimationRepeat()
                    break
                default:
                    //stop and finish
                    button.setTitle("Animate", for: .normal)
                    stopAnimationAndFinish()
                    break
                }
            }
    
        }
    
        func rotateAnimationRepeat(){
            //just to be sure because of how i did the project
            animationView.layer.removeAllAnimations()
            let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
            rotation.fromValue = 0
            rotation.toValue =  Double.pi * 2
            rotation.duration = 0.5
            rotation.repeatCount = Float.greatestFiniteMagnitude
            //not doing cumlative
            animationView.layer.add(rotation, forKey: "rotationAnimation")
        }
    
        func stopAnimationAndFinish(){
            if let presentation = animationView.layer.presentation(){
                if let currentRotation = presentation.value(forKeyPath: "transform.rotation.z") as? CGFloat{
    
                    var duration = 0.5
    
                    //smooth out duration for change
                    duration = Double((CGFloat(Double.pi * 2) - currentRotation))/(Double.pi * 2)
                    animationView.layer.removeAllAnimations()
    
                    let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
                    rotation.fromValue = currentRotation
                    rotation.toValue = Double.pi * 2
                    rotation.duration = duration * 0.5
                    animationView.layer.add(rotation, forKey: "rotationAnimation")
    
                }
            }
        }
    }
    

    Result:

    0 讨论(0)
提交回复
热议问题