User786
User786

Reputation: 25

How to add an animation in progress bar UIView

I want to add an animation in my progress bar which is UIView based. Here is my code.

@IBDesignable class PlainHorizontalProgressBar: UIView {
    @IBInspectable var color: UIColor = .green {
        didSet { setNeedsDisplay() }
    }

    var progress: CGFloat = 0 {
        didSet { setNeedsDisplay() }
    }
    
    private let progressLayer = CALayer()
    private let backgroundMask = CAShapeLayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLayers()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupLayers()
    }
    
    private func setupLayers() {
        layer.addSublayer(progressLayer)
    }
    
    override func draw(_ rect: CGRect) {
        backgroundColor = .grey
        backgroundColor?.setFill()
        
        UIBezierPath(roundedRect: rect, cornerRadius: rect.height * 0.25).fill()
        
        backgroundMask.path = UIBezierPath(roundedRect: rect, cornerRadius: rect.height * 0.25).cgPath
        layer.mask = backgroundMask
    
        let progressRect = CGRect(origin: .zero, size: CGSize(width: rect.width * progress, height: rect.height))
    
        progressLayer.frame = progressRect
        layer.addSublayer(progressLayer)
        progressLayer.backgroundColor = color.cgColor
    }

}

The scenario here is, I have ViewController A and ViewController B, This progress bar I have on ViewController B, I am assigning a value from ViewController A to ViewController B (example: myView.progress = 0.5 ) now when i am coming to ViewController B my progress bar is already filled half but what I want is, when I will come to ViewController B this should animate and filled front of me when I land on viewController B.

Upvotes: 0

Views: 1486

Answers (1)

matt
matt

Reputation: 535138

The main problem here is that your draw implementation is wrong. Or, to put it better, your draw implementation should not exist at all. The only thing that should happen in draw is drawing.

If you're going to add sublayers, you need a specific method that is called just once that adds the sublayers.

If you're going to change the frame of one of those sublayers, you need another specific method that changes the frame of the (already existing) sublayer. As soon as you separate that, it will be a lot more obvious how to animate the change in frame. In fact, because this is a layer, the animation will happen automatically.

enter image description here

class PlainHorizontalProgressBar: UIView {
    
    var color: UIColor = .green {
        didSet { update() }
    }

    var progress: CGFloat = 0 {
        didSet { update() }
    }
    
    private let progressLayer = CALayer()
    private let backgroundMask = CAShapeLayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLayers()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupLayers()
    }
    
    private func setupLayers() {
        layer.addSublayer(progressLayer)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        backgroundMask.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.bounds.size.height * 0.25).cgPath
        layer.mask = backgroundMask
        update()
    }
    
    func update() {
        let progressRect = CGRect(origin: .zero, size: CGSize(width: self.bounds.size.width * progress, height: self.bounds.size.height))
        progressLayer.frame = progressRect
        progressLayer.backgroundColor = color.cgColor
    }

}

The part about being allowed to set progress values between 1 and 100 is a matter of elementary arithmetic and is left as an exercise for the reader.

Upvotes: 1

Related Questions