Как программно завершить работу NSApp без кодирования состояния окна?

Все приложения OS X, которые поддерживают NSWindowRestoration можно закрыть, выбрав пункт меню "Выйти и закрыть все окна" (Option-Command Q). Это отключает восстановление состояния, и в следующий раз, когда вы откроете приложение, все окна будут в положении по умолчанию.

Пункт меню запускает terminate: метод на NSApplication, Но так же, как и обычное меню "Закрыть приложение" (команда Q).

Как я могу сделать "Выйти и закрыть все окна" программно? Мне действительно нужно закрыть все окна самостоятельно, а затем позвонить terminate:?

Как Apple волшебным образом решает, что делать, если оба действия связаны с одним и тем же terminate: метод?

2 ответа

Решение

Кажется, не существует отличного способа сделать это. Возможно, вы захотите сообщить об ошибке в запрос Apple (вместе с объяснением того, зачем вам это нужно).

Как Apple волшебным образом решает, что делать, если оба действия связаны с одним и тем же terminate: метод?

Ну, глядя на разборку AppKit, кажется, что -[NSApplication terminate:] проверяет, является ли отправитель экземпляром NSMenuItem, Если это так, он проверяет, если его userInterfaceItemIdentifier равно @"NSAlternateQuitMenuItem",

Вы могли бы, я полагаю, создать фиктивный пункт меню с этим идентификатором и передать его в качестве отправителя -terminate:, хотя, поскольку это зависит от деталей реализации, он может сломаться в любое время.

Другим управляющим фактором является настройка "Системные настройки"> "Основные"> "Закрыть окна при выходе из приложения". Это соответствует ключу пользователя по умолчанию NSAlternateQuitMenuItemХотя, опять же, это деталь реализации. Похоже, что вы могли бы установить это до вызова -terminate: а затем, в -applicationWillTerminate: Метод делегата, удалите этот параметр. (Ваши изменения будут связаны с вашим приложением. Они не будут влиять на другие приложения или настройку в Системных настройках.) Конечно, вам нужно будет убедиться, что внезапное завершение отключено, чтобы получить этот вызов метода делегата.

NSApplication может быть подклассом.

Задайте имя основного класса в информации о цели сборки, а затем переопределите-terminate:

      @interface MyApplication : NSApplication
@end

@implementation MyApplication

- (void) terminate:(id)sender
{
    if ([sender isKindOfClass:NSMenuItem.class])
    {
        BOOL alwaysKeeps = [NSUserDefaults.standardUserDefaults boolForKey:@"NSQuitAlwaysKeepsWindows"];
        
        NSMenuItem* item = sender;
        item.identifier = alwaysKeeps ? @"NSAlternateQuitMenuItem" : @"";
    }
    [super terminate:sender];
}

@end

Вы можете проверить отправителя (NSMenuItem) в отладчике и увидеть этого участника._uiidустанавливается на @"NSAlternateQuitMenuItem", когда вы удерживаете клавишу Option при выходе.

В зависимости от предпочтений пользователя системы вам необходимо либо добавить, либо удалить идентификатор.

Я бы использовал эту технику только для противоположного эффекта; всегда кодировать восстанавливаемое состояние открытых окон, игнорируя предпочтения пользователя системы. В противном случае я бы просто поставил-terminateдействие где-то в цепочке ответчика, чтобы закрыть все окна до того, как NSApplication получит сообщение:

      - (void) terminate:(id)sender
{
    for (NSWindow* window in NSApp.windows)
    {
        [window close];
    }
    [NSApp terminate:sender];
}
Другие вопросы по тегам