Mac OS X:在NSView坐标和全局屏幕坐标之间转换

时间:2014-02-13 10:10:24

标签: macos core-graphics coordinate-systems coordinate-transformation

我的多显示器设置中存在以下情况:

OSX coordinate conversions

在这个例子中,我想将窗口精确地定位在用黄色箭头描绘的坐标处。然而,我所拥有的是NSView的坐标,它是NSWindow的contentView的子视图,跨越整个(较大的,较高的)辅助监视器。

以下是定义全局坐标空间的方法:

  • {0,0}是笔记本电脑屏幕左上角的坐标。 (绿色)
  • { - 296,-1080}是我第二个屏幕左上角的坐标(黑色)
  • {0,800}是左下角的坐标(此处没有箭头)

因此,y从绿色箭头开始逐渐增加,从绿色箭头开始逐渐减少。

问题:

如何将黄色箭头({100,100},NSScreen内的NSWindow内部的NSView)描绘的点转换为该全局坐标系。 (注意:在NSView中,坐标系左下角有{0,0},向上增加。)

我相信正确答案是{-196,-980},但是在任何屏幕上为任何窗口获取此转换的代码是什么?

我已经在这个问题上花了太多时间,所以非常感谢任何帮助。

(不确定是否相关,但底部屏幕有视网膜分辨率显示。)

2 个答案:

答案 0 :(得分:26)

Mac OS在不同的地方使用不同的坐标系。视图可以定义它们是否具有向上或向下指向的y轴(isFlipped)。窗口的原点以“屏幕坐标”表示,并带有向上的y轴。屏幕使用全局坐标系排列,y指向下方。

最好不要尝试自己转换所有坐标空间,但让责任对象完成工作:

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: ...];

答案 1 :(得分:3)

接受答案的Swift(4.0)版本基本相同:

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: ...)

以下是另一种方法:

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: ...)

后一种方法只是提前设置矩形。对于那些喜欢演练的人来说,这是一个逐个播放的过程:

<强> 1。创建一个矩形。在视图坐标系中的所需位置初始化一个零大小的矩形。

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

<强> 2。从视图转换为窗口。通过为目标nil指定view,将矩形从视图的坐标系转换为窗口的坐标系。

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

第3。从窗口转换为屏幕。接下来,将矩形从窗口的坐标系转换为屏幕的坐标系。

请注意,someView.window可能是nil,因此我们使用可选链接(即?中的window?)并回退到{rect的原始值1}}如果是这样的话。这可能不是必要的,但这是一个很好的习惯。

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

<强> 4。设置矩形的大小。使用所需的新窗口大小更新矩形。

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

<强> 5。创建窗口。使用转换后的矩形初始化一个新窗口。

let newWindow = NSWindow(contentRect: rect, styleMask: ...)