CAKeyframeAnimation don't save the completed view when the animation first starts

21 Views Asked by At

I'm designing an animation so that the 5 objects can move at a 45 degree angle along an arc each time the red circle is clicked.

However, use animation.fillMode = CAMediaTimingFillMode.forwards , but when I try the first animation, the view is not saved and returns to the beginning.

And after that, the touch animation is applied to the second element, the orange circle, and not the red one.

As expected, there seemed to be a difference between the view seen after the view animation and the actual view, but I couldn't figure out why.

Does anyone know about the cause of this?

I am attaching the code and a gif image that actually works with this code. I'm a former Android developer, so I'm relatively weak on iOS.


import UIKit

class ArcMenuTest: UIView {

    
    private lazy var circle1View: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .red
        view.clipsToBounds = true
        return view
    }()
    
    private lazy var circle2View: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .orange
        view.clipsToBounds = true
        return view
    }()
    
    private lazy var circle3View: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .yellow
        view.clipsToBounds = true
        return view
    }()
    
    private lazy var circle4View: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .green
        view.clipsToBounds = true
        return view
    }()
    
    private lazy var circle5View: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .blue
        view.clipsToBounds = true
        return view
    }()
    
    var circle1Radian = 180
    var circle2Radian = 225
    var circle3Radian = 270
    var circle4Radian = 315
    var circle5Radian = 360
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.setup()
        setListener()
    }
    
    private func setup() {
        self.addSubview(self.circle1View)
        NSLayoutConstraint.activate([
            self.circle1View.widthAnchor.constraint(equalToConstant: 30),
            self.circle1View.heightAnchor.constraint(equalToConstant: 30)
        ])
        
        self.addSubview(self.circle2View)
        NSLayoutConstraint.activate([
            self.circle2View.widthAnchor.constraint(equalToConstant: 30),
            self.circle2View.heightAnchor.constraint(equalToConstant: 30)
        ])
        
        self.addSubview(self.circle3View)
        NSLayoutConstraint.activate([
            self.circle3View.widthAnchor.constraint(equalToConstant: 30),
            self.circle3View.heightAnchor.constraint(equalToConstant: 30)
        ])
        
        self.addSubview(self.circle4View)
        NSLayoutConstraint.activate([
            self.circle4View.widthAnchor.constraint(equalToConstant: 30),
            self.circle4View.heightAnchor.constraint(equalToConstant: 30)
        ])
        
        self.addSubview(self.circle5View)
        NSLayoutConstraint.activate([
            self.circle5View.widthAnchor.constraint(equalToConstant: 30),
            self.circle5View.heightAnchor.constraint(equalToConstant: 30)
        ])
        
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        self.circle1View.layer.cornerRadius = self.circle1View.frame.width / 2.0
        self.circle1View.center = self.getPoint(for: 180)
        
        self.circle2View.layer.cornerRadius = self.circle1View.frame.width / 2.0
        self.circle2View.center = self.getPoint(for: 225)
        
        self.circle3View.layer.cornerRadius = self.circle1View.frame.width / 2.0
        self.circle3View.center = self.getPoint(for: 270)
        
        self.circle4View.layer.cornerRadius = self.circle1View.frame.width / 2.0
        self.circle4View.center = self.getPoint(for: 315)
        
        self.circle5View.layer.cornerRadius = self.circle1View.frame.width / 2.0
        self.circle5View.center = self.getPoint(for: 360)

    }
    
    private func getPoint(for angle: Int) -> CGPoint {
        
        let arcCenter = CGPoint(x: bounds.size.width / 2, y: bounds.size.height)
        let circleRadius = bounds.size.width / 2
        let circlePath = UIBezierPath(arcCenter: arcCenter, radius: circleRadius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 2, clockwise: true)
        
        // 1
        let radius = bounds.size.width / 2
        
        // 2
        let radian = Double(angle) * Double.pi / Double(180)
        
        // 3
        let newCenterX = radius + radius * cos(radian)
        let newCenterY = radius + radius * sin(radian)
        
        return CGPoint(x: newCenterX, y: newCenterY)
    }
    
    private func animate(view: UIView, path: UIBezierPath) {
        
        
        // 1
        let animation = CAKeyframeAnimation(keyPath: "position")
        
        // 2
        animation.path = path.cgPath
        
        // 3
        animation.repeatCount = 1
        
        // 4
        animation.autoreverses = false
        animation.duration = 0.5
        animation.fillMode = CAMediaTimingFillMode.forwards;
        animation.isRemovedOnCompletion = false

        // 5
        CATransaction.begin()
        view.layer.add(animation, forKey: "animation")
        
        CATransaction.setDisableActions(true)

        CATransaction.setCompletionBlock({
            if(view == self.circle1View) {
                self.circle1Radian += 45
                self.circle1View.center = self.getPoint(for: self.circle1Radian)
            } else if(view == self.circle2View) {
                self.circle2Radian += 45
                self.circle2View.center = self.getPoint(for: self.circle2Radian)
            } else if(view == self.circle3View) {
                self.circle3Radian += 45
                self.circle3View.center = self.getPoint(for: self.circle3Radian)
            } else if(view == self.circle4View) {
                self.circle4Radian += 45
                self.circle4View.center = self.getPoint(for: self.circle4Radian)
            } else if(view == self.circle5View) {
                self.circle5Radian += 45
                self.circle5View.center = self.getPoint(for: self.circle5Radian)
            }
        })

        CATransaction.commit()

    }
    
    func updateView() {
        
        circle1Radian += 45
        circle2Radian += 45
        circle3Radian += 45
        circle4Radian += 45
        circle5Radian += 45
        
//        self.circle1View.center = self.getPoint(for: circle1Radian)
//        self.circle2View.center = self.getPoint(for: circle2Radian)
//        self.circle3View.center = self.getPoint(for: circle3Radian)
//        self.circle4View.center = self.getPoint(for: circle4Radian)
//        self.circle5View.center = self.getPoint(for: circle5Radian)

    }
    
        
    func startAnimating(uiView : UIView, radius : Int) {
        // 1
        let path = UIBezierPath()
        
        // 2
        let initialPoint = self.getPoint(for: radius)
        let endPoint = radius + 45
        uiView.center = self.getPoint(for: endPoint)

        path.move(to: initialPoint)
        
        
        // 3
        for angle in radius...endPoint {
            path.addLine(to: self.getPoint(for: angle))
        }
        //for angle in 1...270 { path.addLine(to: self.getPoint(for: angle)) }
        
        // 4
        path.close()
        
        // 5
        self.animate(view: uiView, path: path)

    }
    
    
    private func setListener() {
        circle1View.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onCircle1Click)))
    }
    
    @objc private func onCircle1Click() {
        print("??????")
        startAnimating(uiView: circle1View, radius: circle1Radian)
        startAnimating(uiView: circle2View, radius: circle2Radian)
        startAnimating(uiView: circle3View, radius: circle3Radian)
        startAnimating(uiView: circle4View, radius: circle4Radian)
        startAnimating(uiView: circle5View, radius: circle5Radian)
        //updateView()
    }
}

enter image description here

0

There are 0 best solutions below