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.
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.