Mac OS X: Convert between NSView coordinates and global screen coordinates

半世苍凉 提交于 2019-12-03 22:55:50

Mac OS uses different coordinate systems in various places. Views can define if they have an upwards or downwards pointing y-axis (isFlipped). A window's origin is expressed in "screen coordinates" with an upwards y-axis. Screens are arranged using the global coordinate system with y pointing down.

It's better not to try to do the conversion of all the coordinate spaces yourself but let the responsible objects do the job:

NSView *yellowView; // your view that contains the point with the yellow arrow.
NSPoint yellowPoint = { 100, 100 };

NSPoint pointInWindow = [yellowView convertPoint:yellowPoint toView:nil];
NSPoint pointOnScreen = [[yellowView window] convertRectToScreen:(CGRect){.origin=pointInWindow}];

NSWindow *newWindow = [[NSWindow alloc] initWithContentRect:(CGRect){ pointOnScreen, {32, 32}} styleMask: ...];

The Swift (4.0) version of the accepted answer is basically the same:

let yellowView: NSView // your view that contains the point with the yellow arrow.
let yellowPoint = NSPoint(x: 100, y: 100)

let pointInWindow = yellowView.convert(yellowPoint, to: nil)
let pointOnScreen = yellowView.window?.convertToScreen(NSRect(origin: pointInWindow, size: .zero)).origin ?? .zero

let contentRect = NSRect(origin: pointOnScreen, size: NSSize(width: 32, height: 32))
let newWindow = NSWindow(contentRect: contentRect, styleMask: ...)

The following is an alternate way to do it:

let someView: NSView // Some existing view
var rect: NSRect

rect = NSRect(x: 100, y: 100, width: 0, height: 0)
rect = someView.convert(rect, to: nil)
rect = someView.window?.convertToScreen(rect) ?? rect
rect.size = NSSize(width: 32, height: 32)
let newWindow = NSWindow(contentRect: rect, styleMask: ...)

The latter way simply sets the rect up ahead of time. Here's a play-by-play for those who like walkthroughs:

1. Create a rectangle. Initialize a zero-sized rectangle at the desired location within the view's coordinate system.

let someView: NSView // Some existing view
var rect = NSRect(x: 100, y: 100, width: 0, height: 0)

2. Convert from view to window. Convert the rectangle from the view's coordinate system to the window's coordinate system by specifying nil for the destination view.

rect = someView.convert(rect, to: nil)

3. Convert from window to screen. Next, convert the rectangle from the window's coordinate system to the screen's coordinate system.

Note that someView.window may be nil, so we use optional chaining (i.e. the ? in window?) and fallback to the original value of rect if that's the case. This probably isn't necessary, but it's a good habit to get into.

rect = someView.window?.convertToScreen(rect) ?? rect

4. Set the rectangle's size. Update the rectangle with the desired size of the new window.

rect.size = NSSize(width: 32, height: 32)

5. Create the window. Initialize a new window with the converted rectangle.

let newWindow = NSWindow(contentRect: rect, styleMask: ...)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!