How to set the window size and position programmatically for a SpriteKit/GameScene app on OSX
Asked Answered
H

2

5

I have a bare-bones project created in Xcode as a SpriteKit/GameScene app. I want to set the window size programmatically. I've read a lot of answers here, and several tutorials elsewhere, but none of the things I've read have helped.

This answer talks about overriding WindowController.windowDidLoad, but GameScene doesn't give me a WindowController. It does give me a ViewController. This tutorial says you can call self.view.window?.setFrame() from ViewController.viewDidLoad(), but my window stubbornly remains the same size. A number of the answers I've found on SO talk about auto-layout. I don't have anything to lay out. It's just a SpriteKit starter project.

This answer says you can set preferredContentSize in ViewController.viewWillAppear(), and that does in fact let me set the window size, but if I make the window too big (because I had to guess at a legitimate size), it's not draggable or resizable. This answer says you can get the correct size from view.bounds.size, but that says 800 x 600, which is nowhere near the size of my screen. This answer says you can get the bounds from UIScreen.mainScreen().bounds.size, but my GameScene/SpriteKit starter project seems not to have any kind of UIScreen in it.

How can I get the screen size, and how can I set the window size and position programmatically?

Also, one of the other answers, which I can't seem to find any more, says you should delete GameScene.sks, which I did, and everything seems fine still, except for the size.

Hebetude answered 6/11, 2018 at 23:34 Comment(3)
"GameScene doesn't give me a WindowController" Que!? The base view controller should be connected its window controller.Street
Thanks, but I don't know what those words mean, or how I could make use of them. CheersHebetude
If your project didn't give you a window controller, you can add one through Interface Builder for yourself.Street
H
3

It would be awesome if some UI guru could comment on whether this is the "right" answer, but here's what I did as a wild guess based on "El Tomato's" rather cryptic comments. First, add the following to ViewController.swift:

class WildGuessWindowController: NSWindowController {
    override func windowDidLoad() {
        if let screenSize = window?.screen?.frame {
            window!.setFrame(screenSize, display: true)
            print(screenSize)
        }

        super.windowDidLoad()
    }
}

Next, open Main.storyboard in the project navigator. You might see something like the following: enter image description here

Click on the box that says "Window Controller", and open the right-hand side panel. You should see something like this.

enter image description here

Notice the box next to the arrow, that has a grayed-out class name in it. (Also notice that you might have to click the "Identity inspector" button, the blue one above where it says "Custom Class".) Now click that drop-down, and you should see WildGuessWindowController. Select that, and you're set. Build and run. The framework will instantiate your class, and run windowDidLoad().

Hebetude answered 7/11, 2018 at 5:12 Comment(0)
I
3

Updated for Swift 5

Window Size

Remember to add NSWindowDelegate to your NSViewController class if you wish to implement it there (ie. viewDidLoad(), viewWillAppear(), viewDidAppear(), etc.)

NSView

class ViewController: NSViewController, NSWindowDelegate {

    override func viewWillAppear() {
        fillWindow()
    }
    
    /// Sizes window to fill max screen size
    func fillWindow() {
        if let screenSize = view.window?.screen?.frame {
            view.window!.setFrame(screenSize, display: true)
        }
    }

}

NSWindow

class WindowController: NSWindowController {

    override func windowDidLoad() {
        super.windowDidLoad()
    
        fillWindow()
    }

    /// Sizes window to fill max screen size
    func fillWindow() {
        if let screenSize = window?.screen?.frame {
            window!.setFrame(screenSize, display: true)
        }
    }

}

Print to Debugger Console

print(screenSize)    // embed within if let screenSize { ... }

Window Position

See the full answer, with implemented code, here.

extension NSWindow {
    
    /// Positions the `NSWindow` at the horizontal-vertical center of the `visibleFrame` (takes Status Bar and Dock sizes into account)
    public func positionCenter() {
        if let screenSize = screen?.visibleFrame.size {
            self.setFrameOrigin(NSPoint(x: (screenSize.width-frame.size.width)/2, y: (screenSize.height-frame.size.height)/2))
        }
    }
    /// Centers the window within the `visibleFrame`, and sizes it with the width-by-height dimensions provided.
    public func setCenterFrame(width: Int, height: Int) {
        if let screenSize = screen?.visibleFrame.size {
            let x = (screenSize.width-frame.size.width)/2
            let y = (screenSize.height-frame.size.height)/2
            self.setFrame(NSRect(x: x, y: y, width: CGFloat(width), height: CGFloat(height)), display: true)
        }
    }
    /// Returns the center x-point of the `screen.visibleFrame` (the frame between the Status Bar and Dock).
    /// Falls back on `screen.frame` when `.visibleFrame` is unavailable (includes Status Bar and Dock).
    public func xCenter() -> CGFloat {
        if let screenSize = screen?.visibleFrame.size { return (screenSize.width-frame.size.width)/2 }
        if let screenSize = screen?.frame.size { return (screenSize.width-frame.size.width)/2 }
        return CGFloat(0)
    }
    /// Returns the center y-point of the `screen.visibleFrame` (the frame between the Status Bar and Dock).
    /// Falls back on `screen.frame` when `.visibleFrame` is unavailable (includes Status Bar and Dock).
    public func yCenter() -> CGFloat {
        if let screenSize = screen?.visibleFrame.size { return (screenSize.height-frame.size.height)/2 }
        if let screenSize = screen?.frame.size { return (screenSize.height-frame.size.height)/2 }
        return CGFloat(0)
    }

}

Usage

NSWindow

Positions the existing window to the center of visibleFrame.

window!.positionCenter()

Sets a new window frame, at the center of visibleFrame, with dimensions

window!.setCenterFrame(width: 900, height: 600)

NSView

Using xCenter() and yCenter() to get the central x-y points of the visibleFrame.

let x = self.view.window?.xCenter() ?? CGFloat(0)
let y = self.view.window?.yCenter() ?? CGFloat(0)
self.view.window?.setFrame(NSRect(x: x, y: y, width: CGFloat(900), height: CGFloat(600)), display: true)
Ikeda answered 10/2, 2021 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.