2

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?

Oleg
  • 61
  • 5
  • That’s not a rectangle, that’s known as a “capsule” shape. You just have to modify your UI bezier path, fundamentally it’s an arc (for one semi circle “cap”) followed by a line (for one of the flat edges), followed by another arc (for the other “cap”), followed by a final line (for the second flat edge) – Alexander Oct 14 '21 at 18:00
  • Apple usually calls those shapes rounded rectangles. You can create those using the UIBezierPath method `init(roundedRect: CGRect, cornerRadius: CGFloat)` (And specifying a corner radius of 1/2 the size of the shorter side of your rectangle.) – Duncan C Oct 14 '21 at 18:57
  • @DuncanC does not change the circle – Oleg Oct 15 '21 at 07:07

1 Answers1

1

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)
    }
}
RTXGamer
  • 3,215
  • 6
  • 20
  • 29
  • I'm trying this on my gif 2, but how can to proportionally increase from view to the screen edges? like circles – Oleg Oct 15 '21 at 06:44
  • I changed let targetWidth = self.bounds.size.height*2.5 and have bad proportionally increase – Oleg Oct 15 '21 at 07:01
  • @Oleg your desired output didnt reflect that. I'll update the ans. – RTXGamer Oct 15 '21 at 08:50
  • ,thank you! How can I delete this animation and make slowly for example 20x slowly? delete like this: newLayer.removeAllAnimations() animationGroup.animations = nil? – Oleg Oct 15 '21 at 19:34
  • @Oleg You can increase the `animationDuration` and change timingFunction to `linear` to get slower animation. – RTXGamer Oct 16 '21 at 01:13
  • I change duration in pulsate function and its work But how to delete? – Oleg Oct 16 '21 at 09:30
  • https://stackoverflow.com/questions/69594479/swift-stop-cabasicanimation – Oleg Oct 16 '21 at 10:23
  • @Oleg Check the answer I've posted there. – RTXGamer Oct 16 '21 at 11:11