0
votes

I'm creating a bare-bones window programmatically and I just need to draw some lines in its content view. The size of the area the lines fall in (worldBounds) needs to be scaled down and I assume I need the contentView bounds origin to be the same as worldBounds origin so it can draw everything. The approach is to set the frame to 1/8 the size, then use setBoundsOrigin and setBoundsSize on the contentView, which I understand scales the coordinate system.

The problem is no drawing appears. The window appears with the correct size. I have a function drawTestLines set up just to test it. If I remove the calls to setBounds, everything draws fine, but of course then the bounds don't match worldBounds. Any help is much appreciated!

var worldBounds = NSRect()
let scale: CGFloat = 0.125

func drawMap() {

    boundLineStore(lineStore, &worldBounds)
    worldBounds.origin.x -= 8
    worldBounds.origin.y -= 8
    worldBounds.size.width += 16
    worldBounds.size.height += 16

    if !draw {
        return
    }

    let scaled = NSRect(x: 300.0,  // to set the window size/position
                        y: 80,
                        width: worldBounds.size.width*scale,   // 575
                        height: worldBounds.size.height*scale) // 355

    window = NSWindow(contentRect: scaled,
                      styleMask: .titled,
                      backing: .buffered,
                      defer: false)
    window.display()
    window.orderFront(nil)

    window.contentView!.setBoundsSize(worldBounds.size)     // (4593, 2833)
    window.contentView!.setBoundsOrigin(worldBounds.origin) // (-776, -4872)

    // Draw map lines
    window.contentView!.lockFocus()
    drawTestLines(in: window.contentView!)
    window.contentView!.unlockFocus()
}

// for testing
func drawTestLines(in view: NSView) {

    let bottom = view.bounds.minY
    let top = view.bounds.maxY
    let left = view.bounds.minX
    let right = view.bounds.maxX

    NSColor.black.setStroke()
    for i in Int(left)..<Int(right) { // draw vertical lines across the view just to see if it works!
        if i%64 == 0 {
            NSBezierPath.strokeLine(from: NSPoint(x: CGFloat(i), y: bottom), to: NSPoint(x: CGFloat(i), y: top))
        }
    }
    NSBezierPath.strokeLine(from: NSPoint(x: left, y: bottom), to: NSPoint(x: right, y: top))
    NSBezierPath.strokeLine(from: NSPoint.zero, to: NSPoint(x: 50.0, y: 50.0))
}
1
This is not how you draw in Cocoa. You never (ever!) lock focus on a view and draw directly. You override drawRect and let the framework tell your view when it's time to draw its content. See Drawing View ContentJames Bucanek
If origin.y is -4872 and height is 2833 you won't see anything. Don't change contentView.bounds.origin. See Understanding a View's Frame and Bounds.Willeke
Thanks, Willeke. The problem I'm having is that the objects to be drawn have negative coordinates, how does one handle that if the origin must be (0,0)? I've tried just adding a subview with these bounds instead, still nothing.Thomas Foster

1 Answers

0
votes

Experiment: Create a new Xcode project. Change applicationDidFinishLaunching:

func applicationDidFinishLaunching(_ aNotification: Notification) {
    if let contentBounds = window.contentView?.bounds {
        let scale: CGFloat = 0.5
        let worldBounds = CGRect(x: 0.0, y: 0.0, width: contentBounds.size.width * scale, height: contentBounds.size.height * scale)
        window.contentView!.bounds = worldBounds
    }
}

Change the class of the content view of the window in IB to MyView. Add a new class MyView, subclass of NSView. Change draw to:

override func draw(_ dirtyRect: NSRect) {
    super.draw(dirtyRect)

    let bottom = self.bounds.minY
    let top = self.bounds.maxY
    let left = self.bounds.minX
    let right = self.bounds.maxX

    NSColor.black.setStroke()
    for i in Int(left)..<Int(right) { // draw vertical lines across the view just to see if it works!
        if i%64 == 0 {
            NSBezierPath.strokeLine(from: NSPoint(x: CGFloat(i), y: bottom), to: NSPoint(x: CGFloat(i), y: top))
        }
    }
    NSBezierPath.strokeLine(from: NSPoint(x: left, y: bottom), to: NSPoint(x: right, y: top))
    NSBezierPath.strokeLine(from: NSPoint.zero, to: NSPoint(x: 50.0, y: 50.0))
}

See what happens when you change scale or the origin of worldBounds.