Центрировать программно созданное окно

#cocoa #center #nswindow

#cocoa #центр #nswindow

Вопрос:

Я использовал приведенный здесь пример для создания пользовательского окна без заголовка:

Рисование пользовательского окна в Mac OS X

Я обнаружил, что это единственный способ создать окно без заголовка в Leopard, Snow Leopard и Lion, другие методы не работают должным образом ни в Leopard, ни в Lion. (Если я попытаюсь вызвать окно без заголовка через обычный NSWindow и IB, оно больше не запустится в Leopard)

Пока что это пользовательское окно без заголовка отлично работает везде, но я не могу его центрировать, только жестко зафиксированное положение в Interface Builder.

Довольно легко центрировать обычную реализацию окна NSWindow * с помощью [window center], но я не нашел ничего, что работало бы в этом пользовательском подклассе window, окне, которое не создается из nib через Interface Builder.

Я попробовал несколько вещей из NSWindow, но, похоже, ничего не работает.

Есть идеи?

Ответ №1:

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

Это помещает его буквально в центр экрана, не принимая во внимание пространство, занимаемое dock и строкой меню. Если вы хотите это сделать, измените [[window screen] frame] на [[window screen] visibleFrame] .

Комментарии:

1. Глядя на пример проекта xcode, нет определения «окна». (окно не объявлено). даже если я объявляю ‘window’ для программно созданного ‘RoundWindow’, строка заголовка отображается снова, потому что теперь это обычный класс window.

2. Имейте в виду, что для корректного отображения на нескольких дисплеях вам также необходимо выполнить xPos = [[window screen] frame].origin.x; yPos = [[window screen] frame].origin.y;

3. @Glyph суть того, что вы говорите, верна, но математика неверна. В зависимости от настроек отображения вам нужно выполнить разные вычисления. Например, если второй экран находится слева от главного экрана, вы должны -= xPos. Я все еще выясняю, как правильно выполнить вычисления для всех экранных компоновок.

4. @BenBaron Пожалуйста, добавьте ссылку, когда разберетесь с этой математикой 🙂

5. @Glyph мы, наконец, разобрались с этим. Наше приложение имеет открытый исходный код, поэтому взгляните на методы updateWindowFrame и updateWindowOrigin здесь: github.com/balancemymoney/balance-open/blob/master/Balance /…

Ответ №2:

 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))
        }
    }
}
  

Ответ №3:

Вопрос, вероятно, должен заключаться в том, почему [window center] не работает; но, предполагая, что это так, используйте NSScreen , чтобы получить координаты экрана, выполнить вычисления и напрямую отцентрировать окно.

Комментарии:

1. Берем верхний код из ссылки «Рисование пользовательского окна в Mac OS X», где и как следует использовать NSScreen там?

2. @devilhunter: смотрите @Wekwa — [window screen] возвращает NSScreen значение, в котором окно находится в данный момент. Если вы хотите разместить окно на определенном экране NSScreen , вам будет предоставлен список их всех, основного и т.д.

Ответ №4:

Objective — C в macOS Catalina, версия 10.15.3

более читаемый ответ @Wekwa

 - (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];
}
  

Ответ №5:

Обновлено для Swift 5

Я всегда обнаруживал, что центрирование окна с помощью screen.frame приводит к тому, что окно кажется расположенным слишком близко к нижней части экрана. Это связано с тем, что размер док-станции почти в 3 раза больше, чем строка состояния / меню (примерно 70-80 пикселей против 25 пикселей соответственно).

Это приводит к тому, что окно появляется так, как если бы оно было расположено ниже нижней части экрана, поскольку наши глаза автоматически не настраивают вставки строки состояния (размер 1x) и док-станции (размер ~ 3x).

По этой причине я всегда выбираю использовать с screen.visibleFrame , так как это определенно кажется более центрированным. visibleFrame учитывает размеры строки состояния и док-станции и вычисляет центральные точки для рамки между этими двумя объектами.

 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)
    }

}
  

Использование

NSWindow

Позиционирует существующее окно по центру visibleFrame.

 window!.positionCenter()
  

Устанавливает новую рамку окна в центре visibleFrame с размерами

 window!.setCenterFrame(width: 900, height: 600)
  

NSView

Используя xCenter() и yCenter() , чтобы получить центральные точки x-y 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)
  

Пример функции

 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)
}
  

Ответ №6:

Я наткнулся на ту же проблему. Что сработало для меня, так это установить isRestorable значение false для NSWindow объекта перед вызовом center() .

Ответ №7:

Версия Swift: Можно написать простую статическую служебную функцию для того же

 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)
        }
}