0

I'm trying to create an own progress bar by subclassing NSProgressIndicator. I wrote the code using a Playground in Xcode 6 and it works fine (the content is being drawn correctly).

As soon as I put the class onto the GUI (either as type for a "Custom View" or for an "Indeterminate Progress Indicator") the control does not draw though the drawRect(dirtyRect: NSRect) method has been overridden and is being called by the framework.

Here's my code:

class AbProgressBar : NSProgressIndicator
{
    let drawStep = 10

    var rounded: Bool = true
    var margin: CGFloat = 4.0
    var barColor: NSColor = NSColor.blueColor()
    var barBorderColor: NSColor = NSColor.whiteColor()
    var borderColor: NSColor = NSColor.grayColor()
    var backgroundColor: NSColor = NSColor.blackColor()

    init(coder: NSCoder!)
    {
        println(__FUNCTION__)
        super.init(coder: coder)
    }

    init(frame frameRect: NSRect)
    {
        println("\(__FUNCTION__) with frame \(frameRect)")
        super.init(frame: frameRect)
    }

    override func drawRect(dirtyRect: NSRect)
    {
        println(__FUNCTION__)

        // Here we calculate the total value area from minValue to maxValue in order to find out the percental width of the inner bar
        let area = minValue < 0 && maxValue < 0 ? abs(minValue) + maxValue : abs(maxValue) + abs(minValue)
        let currentPercentageFilled: Double = doubleValue >= maxValue ? maxValue : 100 / area * doubleValue
        let innerWidth = (frame.width - (margin * 2)) / 100 * currentPercentageFilled

        let radOuterX = rounded ? frame.height / 2 : 0
        let radOuterY = rounded ? frame.width / 2 : 0
        let radInnerX = rounded ? (frame.height - margin) / 2 : 0
        let radInnerY = rounded ? innerWidth / 2 : 0

        // The inner frame depends on the width filled by the current value
        let innerFrame = NSRect(x: frame.origin.x + margin, y: frame.origin.y + margin, width: innerWidth, height: frame.height - (margin * 2))

        let pathOuter: NSBezierPath = NSBezierPath(roundedRect: frame, xRadius: radOuterX, yRadius: radOuterY)
        let pathInner: NSBezierPath = NSBezierPath(roundedRect: innerFrame, xRadius: radInnerX, yRadius: radInnerY)

        let gradientOuter: NSGradient = NSGradient(startingColor: NSColor.whiteColor(), endingColor: backgroundColor)

        let gradientInner: NSGradient = NSGradient(startingColor: NSColor.grayColor(), endingColor: barColor)

        gradientOuter.drawInBezierPath(pathOuter, angle: 270.0)

        if(pathInner.elementCount > 0)
        {
            gradientInner.drawInBezierPath(pathInner, angle: 270.0)
        }

        borderColor.set()
        pathOuter.stroke()

        barBorderColor.set()
        pathInner.stroke()
    }
}

Using it within a playground works fine. Setting it as type for a control placed on a UI does NOT work.

Does anybody have any clue what might be wrong?

Edit: Just to add that information: I checked the view using the new "Debug View Hierarchy" feature included in Xcode 6. Result: The control is definitely there.

Alex
  • 1,221
  • 1
  • 10
  • 19
  • The drawRect is being called OK in my test, but the original implementation seems to also be called, afterwards. I replaced your drawing code with just NSColor.redColor().set(); NSBezierPath.fillRect(self.bounds) and it draws it properly, but then the original is drawn on top. – Grimxn Jun 19 '14 at 08:26
  • That's pretty strange... I tried the same (just filing the `frame` with any color) and it works, too. But as soon as I switch back to my original code it doesn't work anymore. And (to increase the confusion): watching the `pathOuter` within Xcode's Debug View shows the correct path... – Alex Jun 19 '14 at 08:56

1 Answers1

0

I figured it out... The problem was that I used the frame property of the super class instead of the dirtyRect parameter passed into the drawRect(...) method.

I just exchanged every access to frame with dirtyRect and now it works.

Looks a bit strange to me because (as far as I understand it) dirtyRect should refer to the same rectangle as frame.

Alex
  • 1,221
  • 1
  • 10
  • 19