Steve
Steve

Reputation: 1153

Swift function not drawing circle in updated view

I have a stepper in my View Controller that updates variables(redd1, greenn1, bluee1) in my UIView. drawRect draws an initial circle and updateColor is meant to draw a new circle on top of it with an updated color. The variables get updated when I call updateColor and I know that they get passed through because when i have their values printed out in updateColor they are correct. updateColor won't draw a new circle.

class UIView1: UIView {

    var redd1 = 0.0;
    var greenn1 = 0.0;
    var bluee1 = 0.0;

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

    override func drawRect(rect: CGRect)
    {
        let circle2 = UIView(frame: CGRect(x: -25.0, y: 10.0, width: 100.0, height:100.0))
        circle2.layer.cornerRadius = 50.0
        let startingColor2 = UIColor(red: (CGFloat(redd1))/255, green: (CGFloat (greenn1))/255, blue: (CGFloat(bluee1))/255, alpha: 1.0)
        circle2.backgroundColor = startingColor2;
        addSubview(circle2);
    }

    func updateColor()
    {
        let circle = UIView(frame: CGRect(x: -25.0, y: 10.0, width: 100.0, height: 100.0))
        circle.layer.cornerRadius = 50.0;
        let startingColor = UIColor(red: (CGFloat(redd1))/255, green: (CGFloat(greenn1))/255, blue: (CGFloat(bluee1))/255, alpha: 1.0)
        circle.backgroundColor = startingColor;
        addSubview(circle);   
    }   
}

Upvotes: 1

Views: 719

Answers (2)

Rob
Rob

Reputation: 437392

Do not call addSubview in drawRect. Only use drawRect if you were going to draw a circle yourself (e.g. by calling stroke of a UIBezierPath). Or if you're going to add circles as subviews (as CAShapeLayer sublayers to the view's layer), the retire drawRect altogether.

But by calling addSubview in updateColor, you're not changing the color, but rather you're adding another subview every time. Likewise, by calling addSubview in drawRect, you're adding another subview there, too, every time the OS calls drawRect. For example, I ran your code, changing the color from black to red, to green, to blue, and triggered drawRect to be called again a few more times, and when I look at the view hierarchy in the view debugger, you can see all of the views in the view hierarchy:

enter image description here

This is a dangerous practice, because those subviews will add up over time, taking up memory.

If you're going to add subviews, I would suggest (a) drawRect is not the right place to be adding subviews; and (b) if you're determined to go the addSubview approach, decide whether you can remove the previous subviews before adding new ones.

Personally, if I wanted drawRect to draw circles of different colors, I would just stroke the UIBezierPath. For example:

class CircleView: UIView {

    var redd1: CGFloat = 0.0 {
        didSet {
            setNeedsDisplay()
        }
    }

    var greenn1 : CGFloat = 0.0 {
        didSet {
            setNeedsDisplay()
        }
    }

    var bluee1: CGFloat = 0.0 {
        didSet {
            setNeedsDisplay()
        }
    }

    override func drawRect(rect: CGRect) {
        let color = UIColor(red: redd1 / 255.0, green: greenn1 / 255.0, blue: bluee1 / 255.0, alpha: 1.0)
        color.setStroke()

        let path = UIBezierPath(arcCenter: CGPoint(x: 75, y: 75), radius: 50, startAngle: 0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
        path.lineWidth = 2
        path.lineCapStyle = .Round

        path.stroke()
    }

}

By the way, you notice that we can retire updateColor entirely. With the above code, if you set either red, green, or blue, it will call setNeedsDisplay, which will trigger the view being redrawn automatically.


You report that the above isn't working. Well, I tried it with this code:

class ViewController: UIViewController {
    @IBOutlet weak var circleView: CircleView!
    @IBOutlet weak var redSlider: UISlider!
    @IBOutlet weak var greenSlider: UISlider!
    @IBOutlet weak var blueSlider: UISlider!

    override func viewDidLoad() {
        super.viewDidLoad()

        redSlider.value = Float(circleView.redd1) / 255.0
        greenSlider.value = Float(circleView.greenn1) / 255.0
        blueSlider.value = Float(circleView.bluee1) / 255.0
    }

    @IBAction func valueChangedForSlider(sender: UISlider) {
        circleView.redd1 = 255.0 * CGFloat(redSlider.value)
        circleView.greenn1 = 255.0 * CGFloat(greenSlider.value)
        circleView.bluee1 = 255.0 * CGFloat(blueSlider.value)
    }
}

And it worked fine:

enter image description here

Upvotes: 2

Tom el Safadi
Tom el Safadi

Reputation: 6746

As you mentioned that the both have the values which they should have. The two circles which you are drawing are exactly on the same postion and both have exactly the same color according to your code snippet. How should they differ? Of course you can't see the two circles when they are having the same color and same position. They are overlayed.

Upvotes: 0

Related Questions