Search code examples
iosswiftuilabeldrawrectsetneedsdisplay

Swift weird behavior when setNeedsDisplay is called


I'm making an app that simulates an engine throttle. When I slide a UISlider, I want the RPM guage pointer to increase and the UILabel inside the RPM guage to update. I have everything working except for one weird part: the text in the UILabel overwrites itself (i.e. it draws itself over itself so that all the values are superimposed over each other and it is unreadable). Here's the slider code in my ViewController:

@IBAction func changeThrottleSetting(sender: UISlider)
{
    compactEngineView.RPMPointerAngleInt = Int(sender.value)
    compactEngineView.setNeedsDisplay()
}

Then in the compactEngineView itself:

var RPMPointerAngleInt = Int()

var RPMPointerAngle: CGFloat { //this angle is used to draw the pointer points later down in the code, didn't show it here just to keep it shorter
    var AngleCGFloat = CGFloat(RPMPointerAngleInt)
    var Pi = CGFloat(M_PI)
    var CorrectedAngle = (9 * Pi / 800) * AngleCGFloat - (Pi/2) //just some math to make it look right on the display
    return CorrectedAngle
}

var RPMPercentageValue: Int {
    var correctedValue = (0.55) * Float(RPMPointerAngleInt) + 55
    return Int(correctedValue)
}

override func drawRect(rect: CGRect)
{
    let RPMPointerPath = UIBezierPath()
    RPMPointerPath.moveToPoint(RPMPointer1)
    RPMPointerPath.addLineToPoint(RPMPointer2)
    RPMPointerPath.addLineToPoint(RPMPointer3)
    RPMPointerPath.addLineToPoint(RPMPointer1)
    whiteColor.set()
    RPMPointerPath.stroke()
    RPMPointerPath.fill()

    //here is the troublemaker
    let RPMdisplaylabel = UILabel(frame: CGRectMake(0, 0, 81, 32))
    RPMdisplaylabel.center = RPMdisplayLocation
    RPMdisplaylabel.textAlignment = NSTextAlignment.Right
    RPMdisplaylabel.text = "\(RPMPercentageValue)%"
    RPMdisplaylabel.textColor = UIColor.whiteColor()
    RPMdisplaylabel.font = UIFont(name: "HelveticaNeue",
        size: 27.0)
    self.addSubview(RPMdisplaylabel)
}

The pointer works perfectly. No complaints. But the text just draws over itself every time I move the slider. Is there a separate trick for UILabels in the DrawRect portion? Thanks!


Solution

  • //here is the troublemaker
    let RPMdisplaylabel = UILabel(frame: CGRectMake(0, 0, 81, 32))
    RPMdisplaylabel.center = RPMdisplayLocation
    RPMdisplaylabel.textAlignment = NSTextAlignment.Right
    RPMdisplaylabel.text = "\(RPMPercentageValue)%"
    RPMdisplaylabel.textColor = UIColor.whiteColor()
    RPMdisplaylabel.font = UIFont(name: "HelveticaNeue",
        size: 27.0)
    self.addSubview(RPMdisplaylabel)
    

    You are doing something that you should never do: in drawRect:, you are doing something more than drawing inside the rect! The job of drawRect: is to draw the content inside the rect and no more or less. It is not the place to add subviews! And you have discovered one reason why: since drawRect: can be called thousands of times during the lifetime of the app, you will end up adding thousands of identical subviews.

    So what's happening here is that you are creating and adding a UILabel, again and again and again and again... So you end up with gazillions of UILabels, all in the same location and thus overlapping each other and giving the result you have rightly described.

    Solution: add the label once, elsewhere. Don't misuse drawRect: for this.