Updated for Swift 5
I always found that centering the window with screen.frame
resulted in a window that feels as if it were too close to the bottom of the screen. This is due to the Dock size being nearly 3x as large as the Status/Menu Bar (roughly 70-80px vs. 25px, respectively).
This causes the window to appear as if it were positioned lower to the bottom of the screen, as our eyes do not automatically adjust the insets of the Status Bar (1x size) and the Dock (~3x size).
For this reason, I always choose to go to with screen.visibleFrame
, as it definitely feels more centered. visibleFrame
takes both the Status Bar and Dock sizes into consideration, and calculates the central points for the frame between those two objects.
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)
Function Example
override func viewDidLoad() {
super.viewDidLoad()
initWindowSize(width: 900, height: 600)
}
func initWindowSize(width: Int, height: Int) {
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(width), height: CGFloat(height)), display: true)
}