swiftanimationrectanglespulse

Swift pulse animation non circle


I'm trying to create pulse animation rectangular shape I have code:

func createPulse() {
    for _ in 0...3 {
        let circularPath = UIBezierPath(arcCenter: .zero, radius: view.bounds.size.height/2, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
        let pulseLayer = CAShapeLayer()
        pulseLayer.path = circularPath.cgPath
        pulseLayer.lineWidth = 2
        pulseLayer.fillColor = UIColor.clear.cgColor
        
        pulseLayer.lineCap = CAShapeLayerLineCap.round
        pulseLayer.position = CGPoint(x: binauralBackView.frame.size.width/2, y: binauralBackView.frame.size.height/2)
        pulseLayer.cornerRadius = binauralBackView.frame.width/2
        binauralBackView.layer.insertSublayer(pulseLayer, at: 0)
        pulseLayers.append(pulseLayer)
    }
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        self.animatePulse(index: 0)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            self.animatePulse(index: 1)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                self.animatePulse(index: 2)
            }
        }
    }
}

func animatePulse(index: Int) {
    pulseLayers[index].strokeColor = #colorLiteral(red: 0.7215686275, green: 0.5137254902, blue: 0.7647058824, alpha: 1).cgColor
    let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
    scaleAnimation.duration = 2
    scaleAnimation.fromValue = 0.0
    scaleAnimation.toValue = 0.9
    scaleAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
    scaleAnimation.repeatCount = .greatestFiniteMagnitude
    pulseLayers[index].add(scaleAnimation, forKey: "scale")
    
    let opacityAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
    opacityAnimation.duration = 2
    opacityAnimation.fromValue = 0.9
    opacityAnimation.toValue = 0.0
    opacityAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
    opacityAnimation.repeatCount = .greatestFiniteMagnitude
    pulseLayers[index].add(opacityAnimation, forKey: "opacity")
}

and have result

circle result

I need to create rectangular like this (like border of image):

rectangular

How can change circle to rectangular?


Solution

  • Output:

    enter image description here

    Note: You can add CABasicAnimation for opacity too in animationGroup if this solution works for you.

    extension UIView {
        /// animate the border width
        func animateBorderWidth(toValue: CGFloat, duration: Double) -> CABasicAnimation {
            let widthAnimation = CABasicAnimation(keyPath: "borderWidth")
            widthAnimation.fromValue = layer.borderWidth
            widthAnimation.toValue = toValue
            widthAnimation.duration = duration
            return widthAnimation
        }
        
        /// animate the scale
        func animateScale(toValue: CGFloat, duration: Double) -> CABasicAnimation {
            let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
            scaleAnimation.fromValue = 1.0
            scaleAnimation.toValue = toValue
            scaleAnimation.duration = duration
            scaleAnimation.repeatCount = .infinity
            scaleAnimation.isRemovedOnCompletion = false
            scaleAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
            return scaleAnimation
        }
        
        func pulsate(animationDuration: CGFloat) {
            
            var animationGroup = CAAnimationGroup()
            animationGroup = CAAnimationGroup()
            animationGroup.duration = animationDuration
            animationGroup.repeatCount = Float.infinity
            
            let newLayer = CALayer()
            newLayer.bounds = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
            newLayer.cornerRadius = self.frame.width/2
            newLayer.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
            newLayer.cornerRadius = self.frame.width/2
            
            animationGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
            
            animationGroup.animations = [animateScale(toValue: 6.0, duration: 2.0),
                                         animateBorderWidth(toValue: 1.2, duration: animationDuration)]
            newLayer.add(animationGroup, forKey: "pulse")
            self.layer.cornerRadius = self.frame.width/2
            self.layer.insertSublayer(newLayer, at: 0)
        }
    }
    

    Usage:

    class ViewController: UIViewController {
        @IBOutlet weak var pulseView: UIView!
        override func viewDidLoad() {
            super.viewDidLoad()
            pulseView.pulsate(animationDuration: 1.0)
        }
    }