Search code examples
iosswiftuiviewuianimation

Fill gradually a shape using swift and UIViewAnimation?


I have an iOS 10 app that contains a button ("Press Me!"), and a UIView containing a blue outlined rectangle, with no fill. I want to fill the rectangle gradually (in like 5 seconds) with red, using any possible method (I tried using UIView.animation(withDuration:, animations:)). I will include the code below:

Here's the viewController:

class ViewController: UIViewController {

@IBOutlet weak var tronkyy: tronc!

override func viewDidLoad() {
    super.viewDidLoad()

}

@IBAction func didPress(_ sender: UIButton) {
    tronkyy.full = true
    tronkyy.setNeedsDisplay()
} }

And the view:

@IBDesignable class tronc: UIView {

//var value = 0.0
var full = false

override func draw(_ rect: CGRect) {
    UIColor.blue.setStroke()
    boxPath().stroke()
    drawDynamic()
}

private func boxPath() -> UIBezierPath {
    let path = UIBezierPath(rect: CGRect(x: 0.1 * frame.width, y: 0.1 * frame.height, width: 0.8 * frame.width, height: 0.8 * frame.height))
    UIColor.blue.setStroke()
    return path
}

func drawDynamic() {
    if full {
        UIView.animate(withDuration: 10) { _ in
            //while self.value < 1 {
                let path = UIBezierPath(rect: CGRect(x: 0.1 * self.frame.width, y: 0.1 * self.frame.height, width: (0.8 * self.frame.width), height: 0.8 * self.frame.height)) //* CGFloat(value)))
                UIColor.red.setFill()
                path.fill()

                //self.value += 0.1
            //}
        }
        full = false
    }
} }

Ah, and yes, please ignore that the red view cover the rectangle. The problem is that there is no gradual filling. it just fills it once and for all.

Edit: I commented out the "value" property and the while loop, as suggested in the answers, but it still doesn't work.


Solution

  • This is an approach using a view with a subview. For a more complex shape than a rectangle, it could be done with masks.

    You can run this directly in a Playground page:

    import UIKit
    import PlaygroundSupport
    
    class TestViewController : UIViewController {
    
        let btn: UIButton = {
            let b = UIButton()
            b.setTitle("Tap Me", for: .normal)
            b.backgroundColor = .blue
            b.frame = CGRect(x: 20, y: 20, width: 200, height: 30)
            return b
        }()
    
        let blueRect: UIView = {
            let v = UIView()
            v.layer.borderColor = UIColor.blue.cgColor
            v.layer.borderWidth = 2.0
            v.backgroundColor = .clear
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        let redRect: UIView = {
            let v = UIView()
            v.backgroundColor = .red
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        var redRectHeightConstraint: NSLayoutConstraint?
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.view.addSubview(btn)
    
            btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
    
    
            blueRect.addSubview(redRect)
            self.view.addSubview(blueRect)
    
            redRect.leftAnchor.constraint(equalTo: blueRect.leftAnchor, constant: 0.0).isActive = true
            redRect.rightAnchor.constraint(equalTo: blueRect.rightAnchor, constant: 0.0).isActive = true
            redRect.bottomAnchor.constraint(equalTo: blueRect.bottomAnchor, constant: 0.0).isActive = true
    
            redRectHeightConstraint = NSLayoutConstraint(item: redRect,
                                                         attribute: .height,
                                                         relatedBy: .equal,
                                                         toItem: nil,
                                                         attribute: .notAnAttribute,
                                                         multiplier: 1.0,
                                                         constant: 0.0)
    
            redRect.addConstraint(redRectHeightConstraint!)
    
            blueRect.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.5).isActive = true
            blueRect.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.5).isActive = true
            blueRect.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
            blueRect.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
    
        }
    
        func didTap(_ sender: Any?) -> Void {
            UIView.animate(withDuration: 5.0, animations: {
                _ in
                self.redRectHeightConstraint!.constant = self.blueRect.frame.size.height
                self.view.setNeedsLayout()
            }, completion: {
                finished in
            })
    
        }
    
    }
    
    
    let vc = TestViewController()
    vc.view.backgroundColor = .yellow
    vc.view.frame = CGRect(x: 0, y: 0, width: 600, height: 600)
    
    PlaygroundPage.current.liveView = vc
    

    Note: this is just sample, demonstration code... not meant to be a copy/paste solution.