Center programmatically created window
I've been using the example from here to create a custom titlebar-less window:
Drawing a custom window on Mac OS X
I've found this is the only way i can create a titlebar-less window in Leopard, Snow Leopard and Lion, other methods don't work right either on Leopard or Lion. (If i try to invoke a titlebar-less window via normal NSWindow and IB, it won't start up in Leopard anymore)
So far this custom titlebar-less window works great everywhere, but i can't center it, only a hard fixed posi开发者_C百科tion in Interface Builder.
It's fairly easy to center a normal NSWindow *window implementation with [window center], but i've found nothing that works on this custom window subclass, a window that isn't created from nib via Interface Builder.
I've tried a few things from NSWindow, but nothing seems to work.
Any Ideas?
CGFloat xPos = NSWidth([[window screen] frame])/2 - NSWidth([window frame])/2;
CGFloat yPos = NSHeight([[window screen] frame])/2 - NSHeight([window frame])/2;
[window setFrame:NSMakeRect(xPos, yPos, NSWidth([window frame]), NSHeight([window frame])) display:YES];
This puts it at the literal center of the screen, not taking into account the space occupied by the dock and menu bar. If you want to do that, change [[window screen] frame]
to [[window screen] visibleFrame]
.
extension NSWindow {
public func setFrameOriginToPositionWindowInCenterOfScreen() {
if let screenSize = screen?.frame.size {
self.setFrameOrigin(NSPoint(x: (screenSize.width-frame.size.width)/2, y: (screenSize.height-frame.size.height)/2))
}
}
}
The question should probably be why [window center]
does not work; but assuming that is the case use NSScreen
to get the screen coordinates, do the math, and center the window directly.
Objective - C in macOS Catalina , Version 10.15.3
more readable @Wekwa's answer
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSWindow * window = NSApplication.sharedApplication.windows[0];
CGFloat xPos = NSWidth(window.screen.frame)/2 - NSWidth(window.frame)/2;
CGFloat yPos = NSHeight(window.screen.frame)/2 - NSHeight(window.frame)/2;
[window setFrame:NSMakeRect(xPos, yPos, NSWidth(window.frame), NSHeight(window.frame)) display:YES];
}
I stumbled on the same issue. What worked for me is to set isRestorable
to false
on the NSWindow
object before calling center()
.
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)
}
Swift version: One can write a simple static utility function for the same
static func positionWindowAtCenter(sender: NSWindow?){
if let window = sender {
let xPos = NSWidth((window.screen?.frame)!)/2 - NSWidth(window.frame)/2
let yPos = NSHeight((window.screen?.frame)!)/2 - NSHeight(window.frame)/2
let frame = NSMakeRect(xPos, yPos, NSWidth(window.frame), NSHeight(window.frame))
window.setFrame(frame, display: true)
}
}
精彩评论