denniss denniss - 1 month ago 18
Swift Question

Custom View Broken When Window is Resized (Cocoa)

I am experiencing this weird bug with my custom view. The custom view is supposed to show meters of rating distribution. It gets added to a cell view of an outline view.

When I resize the window, the custom view somehow gets squished and looks broken. I have pasted the

drawRect
of the custom view below.

enter image description here

override func drawRect(r: NSRect) {
super.drawRect(r)

var goodRect: NSRect?
var okRect: NSRect?
var badRect: NSRect?
let barHeight = CGFloat(10.0)

if self.goodPercent != 0.0 {
goodRect = NSRect(x: 0, y: 0, width: r.width * CGFloat(goodPercent), height: barHeight)
let goodPath = NSBezierPath(roundedRect: goodRect!, xRadius: 6, yRadius: 6)
RatingDistributionView.goodColor.setFill()
goodPath.fill()
}

if self.okPercent != 0.0 {
let okX = CGFloat(goodRect?.width ?? 0.0)
okRect = NSRect(x: okX, y: 0, width: r.width * CGFloat(okPercent), height: barHeight)
let okPath = NSBezierPath(roundedRect: okRect!, xRadius: 6, yRadius: 6)

RatingDistributionView.okColor.setFill()
okPath.fill()
}

if self.badPercent != 0.0 {
var badX: CGFloat
//Cases:
//Good persent and OK present - badX = okRect.x + okRect.width
//Good persent and OK missing - badX = goodRect.x + goodRect.width
//Good missing and OK present - badX = okRect.x + okRect.width
//Both missing -

if okRect != nil {
badX = okRect!.origin.x + okRect!.width
}else if goodRect != nil {
badX = goodRect!.origin.x + goodRect!.width
} else {
badX = 0.0
}

badRect = NSRect(x: badX, y: 0, width: r.width * CGFloat(badPercent), height: barHeight)
let badPath = NSBezierPath(roundedRect: badRect!, xRadius: 6, yRadius: 6)
RatingDistributionView.badColor.setFill()
badPath.fill()
}

//Draw dividers
let divWidth = CGFloat(6.75)

if self.goodPercent != 0.0 && (self.okPercent != 0.0 || self.badPercent != 0.0) {
let divX = goodRect!.origin.x + goodRect!.width
let divRect = NSRect(x: divX - (divWidth / 2.0), y: 0.0, width: divWidth, height: barHeight)
let divPath = NSBezierPath(roundedRect: divRect, xRadius: 0, yRadius: 0)
NSColor.whiteColor().setFill()
divPath.fill()
}

if self.okPercent != 0.0 && self.badPercent != 0.0 {
let divX = okRect!.origin.x + okRect!.width
let divRect = NSRect(x: divX - (divWidth / 2.0), y: 0.0, width: divWidth, height: barHeight)
let divPath = NSBezierPath(roundedRect: divRect, xRadius: 0, yRadius: 0)
NSColor.whiteColor().setFill()
divPath.fill()
}
}

Answer

From the documentation of drawRect(_ dirtyRect: NSRect):

dirtyRect: A rectangle defining the portion of the view that requires redrawing. This rectangle usually represents the portion of the view that requires updating. When responsive scrolling is enabled, this rectangle can also represent a nonvisible portion of the view that AppKit wants to cache.

Don't use dirtyRect for your calculations, use self.bounds.

Comments