Search code examples
swiftcashapelayercabasicanimationcaanimation

Swift - When button pressed, reset the animation to beginning if it is already playing


I have a radial animation on programmatically created button which appear when the button is pressed.

If the button is pressed whilst the animation is already running, I want the animation to reset at the beginning and automatically play again.

In the "animationDidStop" function, I have code which applies when the animation does complete. The issue I have is when the button is pressed and an animation is already running. I can't remove the animation running and start a new one as it calls the "animationDidStop". I don't want this to be called yet.

My ideas was to adjust the animation value attached to the radial shape but this isn't working has planned . I know can access the animation values but which one would I change as "fromValue" doesn't show up. I also don't even know if this would actually work though or a better way. I been stuck for a while now.

 var shapeLayerArray: [CustomShapeLayer] = []

 func AutoUpRadial(button: CustomBtn, height: Int, value: Int){

    let trackLayer = CustomShapeLayer()

    let radius = height / 3
    let circularPath = UIBezierPath(arcCenter: button.center, radius: CGFloat(radius), startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true)
    trackLayer.path = circularPath.cgPath

    trackLayer.strokeColor = UIColor.black.cgColor
    trackLayer.opacity = 0.3
    trackLayer.fillColor = UIColor.clear.cgColor
    trackLayer.lineWidth = 5
    trackLayer.strokeEnd = 0

    mainScrollView.layer.addSublayer(trackLayer)

    autoUpFillRadial(value: value, tmpBtn: button, shape: trackLayer)


        shapeLayerArray.append(trackLayer)  
}

@objc private func autoUpFillRadial(value: Int, tmpBtn: CustomBtn, shape: CustomShapeLayer){

    let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
    basicAnimation.fromValue = 0
    basicAnimation.toValue = 1
    basicAnimation.duration = CFTimeInterval(value)
    basicAnimation.fillMode = .forwards
    basicAnimation.isRemovedOnCompletion = true
    basicAnimation.setValue(tmpBtn.UUIDtag, forKey: "animationID")
    basicAnimation.delegate = self

    shape.add(basicAnimation, forKey: "basic")
}

func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {

    if let tag = anim.value(forKey: "animationID") as? UUID {
        if let tmpButton: CustomBtn = tmpButtonPicked(uuid: tag) {
                    tmpButton.isSelected = false
                    actionInput(sender: tmpButton, act: "up")
        }
    }
}

Below is the button script

@IBAction func clipButton(sender: CustomBtn){

    currentSelectedMarkerUUID = sender.UUIDtag

    if let marker = markUpPlist.arrayObjects.filter({$0.UUIDpic == currentSelectedMarkerUUID}).first {

        if recording {
            if sender.isSelected {

                if !shapeLayerArray.isEmpty {
                    for shape in shapeLayerArray {
                        if shape.uuidTag == marker.UUIDpic {

                            print(shape.animation(forKey: "basic"))
                            // The line aboves shows the animation data

                            sender.isSelected = true
                        } else {
                            sender.isSelected = false
                            endClip(sender: sender)
                        }
                    }
                } else if shapeLayerArray.isEmpty {
                    sender.isSelected = false
                    endClip(sender: sender)
                }

            } else if !sender.isSelected {

                sender.isSelected = true
                startClip(sender: sender)


                if marker.timeAutoUp > 0 {

                    if !shapeLayerArray.isEmpty {
                        for shape in shapeLayerArray {
                            if shape.uuidTag == marker.UUIDpic {

                                //DO SOMETHING

                            } else {
                                self.AutoUpRadial(button: sender, height: marker.height, value: marker.timeAutoUp)
                            }
                        }
                    } else if shapeLayerArray.isEmpty {

                        self.AutoUpRadial(button: sender, height: marker.height, value: marker.timeAutoUp)
                    }
                }
            } else {
                sender.isSelected = false
            }
        }
    }
}

class CustomShapeLayer: CAShapeLayer {
var uuidTag: UUID?
}

So to recap, I want to be able to reset the animation if the animation is already playing when the button is pressed but without calling the animationDidStop function.

I tried to remove as much stuff irrelevant to the code to condense it as much as I could. Hopefully it makes sense. Thanks for any advice


Solution

  • After hours stuck on it I came up with this. I added a reset boolean in my custom CAshapelayer. The reset bool gets called when the button is pressed and the animation is already running.

    class CustomShapeLayer: CAShapeLayer {
    var uuidTag: UUID?
    vart reset = false
    }
    
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
    
        if let tag = anim.value(forKey: "animationID") as? UUID {
            if let tmpButton: CustomBtn = tmpButtonPicked(uuid: tag) {
    
                if !shapeLayerArray.isEmpty {
                    for shape in shapeLayerArray {
                        if shape.uuidTag == tag{
                            if shape.reset {
                                shape.reset = false
                            } else {
                                tmpButton.isSelected = false
                                actionInput(sender: tmpButton, act: "up")
                            }
                        } else {
                                tmpButton.isSelected = false
                                actionInput(sender: tmpButton, act: "up")
                        }
                    }
                } else if shapeLayerArray.isEmpty {
    
                        tmpButton.isSelected = false
                        actionInput(sender: tmpButton, act: "up")
                }
            }
        }
    }