Animate a circle from the center in Swift

Details

  • Xcode 10.2.1 (10E1001), Swift 5

Full Sample

CircleView

class CircleView: UIView {

    weak var circleView: UIView?
    lazy var isAnimating = false

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    private func setup() {
        let rectSide = (frame.size.width > frame.size.height) ? frame.size.height : frame.size.width
        let circleRect = CGRect(x: (frame.size.width-rectSide)/2, y: (frame.size.height-rectSide)/2, width: rectSide, height: rectSide)
        let circleView = UIView(frame: circleRect)
        circleView.backgroundColor = UIColor.yellow
        circleView.layer.cornerRadius = rectSide/2
        circleView.layer.borderWidth = 2.0
        circleView.layer.borderColor = UIColor.red.cgColor
        addSubview(circleView)
        self.circleView = circleView
    }

    func resizeCircle (summand: CGFloat) {

        guard let circleView = circleView else { return }
        frame.origin.x -= summand/2
        frame.origin.y -= summand/2
        frame.size.height += summand
        frame.size.width += summand

        circleView.frame.size.height += summand
        circleView.frame.size.width += summand
    }

    private func animateChangingCornerRadius (toValue: Any?, duration: TimeInterval) {
        guard let circleView = circleView else { return }
        let animation = CABasicAnimation(keyPath:"cornerRadius")
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        animation.fromValue = circleView.layer.cornerRadius
        animation.toValue =  toValue
        animation.duration = duration
        circleView.layer.cornerRadius = circleView.frame.size.width/2
        circleView.layer.add(animation, forKey:"cornerRadius")
    }


    private func circlePulseAinmation(_ summand: CGFloat, duration: TimeInterval, completionBlock:@escaping ()->()) {

        guard let circleView = circleView else { return }
        UIView.animate(withDuration: duration, delay: 0,  options: .curveEaseInOut, animations: { [weak self] in
            self?.resizeCircle(summand: summand)
        }) { _ in completionBlock() }

        animateChangingCornerRadius(toValue: circleView.frame.size.width/2, duration: duration)
    }

    func resizeCircleWithPulseAinmation(_ summand: CGFloat,  duration: TimeInterval) {
        if (!isAnimating) {
            isAnimating = true
            circlePulseAinmation(summand, duration:duration) { [weak self] in
                guard let self = self else { return }
                self.circlePulseAinmation((-1)*summand, duration:duration) {self.isAnimating = false}
            }
        }
    }
}

ViewController

import UIKit

class ViewController: UIViewController {

    weak var circleView: CircleView?
    weak var button: UIButton?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let circleView = CircleView(frame: CGRect(x: 40, y: 50, width: 40, height: 60))
        circleView.backgroundColor = UIColor.clear
        view.addSubview(circleView)
        self.circleView = circleView

        let button = UIButton(frame: CGRect(x: 20, y: 150, width: 80, height: 40))
        button.setTitle("Animate", for: UIControl.State())
        button.setTitleColor(UIColor.blue, for: UIControl.State())
        button.setTitleColor(UIColor.blue.withAlphaComponent(0.3), for: .highlighted)
        button.addTarget(self, action: #selector(ViewController.animateCircle), for: .touchUpInside)
        view.addSubview(button)
        self.button = button
    }

    @objc func animateCircle() {
        circleView?.resizeCircleWithPulseAinmation(30, duration: 1.5)
    }
}

Result

enter image description here


You should be able to do it a lot simpler using the transform property.

func pulse() {
    UIView.animate(withDuration: 0.5, animations:{
        self.circleView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
    }, completion: { _ in
        UIView.animate(withDuration: 0.5, animations: {
            self.circleView.transform = .identity
        })
    })
}