Mac OS X: преобразование координат NSView и глобальных экранных координат
У меня есть следующая ситуация в моей настройке нескольких мониторов:
![OSX coordinate conversions]()
В этом примере я хочу расположить окно точно в координатах, обозначенных желтой стрелкой. Тем не менее, все, что у меня есть, - это координаты NSView, который является подвидным содержимым ContentView NSWindow, который охватывает весь (больший, верхний) дополнительный монитор.
Здесь определяется глобальное пространство координат:
- {0,0} - координата верхнего левого угла экрана ноутбука. (Зеленый)
- {- 296, -1080} - координата верхнего левого угла моего второго экрана (черный)
- {0, 800} - координата нижнего левого угла (здесь нет стрелки)
Таким образом, y растет вниз с зеленой стрелки, уменьшаясь вверх от зеленой стрелки.
Проблема:
Как преобразовать точку, обозначенную желтой стрелкой ({100,100}, NSView внутри NSWindow внутри этого NSScreen) в эту глобальную систему координат. ( Примечание: в NSView система координат имеет {0,0} в левом нижнем углу, увеличиваясь вверх.)
Я верю, что правильный ответ - {-196, -980}, но зачем код для этого преобразования для любого окна на любом экране?
Я потратил слишком много времени на эту проблему, поэтому любая помощь очень ценится.
(Не уверен, если это уместно, но на нижнем экране отображается разрешение сетчатки.)
Ответы
Ответ 1
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: ...];
Ответ 2
Версия принятого ответа 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
, если это случай. Это, вероятно, не обязательно, но это хорошая привычка вступать.
rect = someView.window?.convertToScreen(rect) ?? rect
4. Задайте размер прямоугольника. Обновите прямоугольник нужного размера в новом окне.
rect.size = NSSize(width: 32, height: 32)
5. Создайте окно. Инициализируйте новое окно с преобразованным прямоугольником.
let newWindow = NSWindow(contentRect: rect, styleMask: ...)