Как дождаться завершения листа после вызова beginSheet:completionHandler:

#xcode #cocoa

#xcode #cocoa

Вопрос:

В результате моего кода лист отображается и функционирует должным образом, но вызывающий получает управление обратно (согласно определению Apple) до того, как лист завершается endSheet .

Как я могу заставить вызывающее устройство дождаться возврата из конца обработки листа, чтобы resultValue обновлялось.

Вызывающий:

 [self.window beginSheet: sheetController.window
    completionHandler:^(NSModalResponse returnCode) {
        resultValue = returnCode;
    }
];
...
  

Лист:

 ...
[self.window.sheetParent endSheet:self.window returnCode:false];
  

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

1. Не ждите. Всегда есть способ обработать асинхронное поведение.

2. @vadian Итак, как бы вы справились с этой ситуацией, когда больше нечего делать, но вам дали возможность что-то делать?

3. Если вы говорите о модальном листе, вы можете использовать расширение класса, которое выполняет beginSheetModalForWindow with stopModalWithCode в обработчике завершения, за которым следует runModalForWindow (я не использую Obj-C / Swift для примера).

4. @red_menace Изначально я заставлял приложение работать с помощью beginSheet:ModalForWindow и т.д., Но теперь оно устарело, и я пытаюсь каким-то образом реализовать предложенную Apple замену, как указано выше, но документация не полная и не понятная. И да, я хочу поведение модального типа для листа.

5. Мне интересно, не неправильно ли я использовал или разместил вызов `endSheet:’? Спасибо.

Ответ №1:

Я думал, что new beginSheet: предназначался для выполнения всей компоновки, но это не так. Все, что он делает, это выводит окно sheet. Передача и возврат элемента управления остаются такими, как это было. Итак, работает следующий код:

Вызывающий:

 [self.window beginSheet: sheetController.window
    completionHandler:nil
];
returnCodeValue = [NSApp runModalForWindow:sheetController.window];
    // Sheet is active until stopModalWithCode issued.
[NSApp endSheet: sheetController.window];
[sheetController.window orderOut:self];
  

Лист:

 [NSApp stopModalWithCode:whatever];
  

Ответ №2:

Похоже, вы прошли большую часть пути. Код возврата должен иметь тип NSModalResponse (ObjC) или NSApplication.ModalResponse (Swift). Я также обнаружил, что вам нужно снять флажок «Виден при запуске». В противном случае он не будет запущен как модальный.

Параметры окна

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

1. Хорошо, да, это настроено соответствующим образом, но безрезультатно. Но проблема, похоже, существует в архитектуре среды, где асинхронный процесс (модальный sheet) может быть инициирован, по-видимому, без какого-либо способа приостановить вызывающее устройство до завершения модального. Непризнание такой часто нормальной ситуации продемонстрировало бы глубокое непонимание управления, требуемого в асинхронной среде, управляемой событиями. Итак, если не существует какого-либо способа решить эту проблему, предоставленного инженерами Apple, они будут казаться некомпетентными. Я не могу в это поверить, так где же решение?

2. А, кажется, теперь я понимаю вашу проблему. Поправьте меня, если я ошибаюсь, но вы считаете, что код должен блокироваться в момент вызова beginSheet. Поскольку это делается в главном потоке, это означало бы, что весь интерфейс будет заблокирован, поскольку все события пользовательского интерфейса и связанный с ними код происходят в главном потоке. Как упоминалось в комментариях выше, вам необходимо реструктурировать свой код таким образом, чтобы вместо процедурного выполнения всех шагов по порядку ваша система реагировала на действия пользователя. Вам нужно будет разбить ваш код на специализированные функции, которые могут быть вызваны из обработчика завершения.

3. Вы близки к истине. Я нашел решение в предыдущей версии кода. Смотрите мой собственный ответ ниже.

4. Он выполняет то, что вы искали, но категория не требуется. Вся функциональность встроена в NSAlert.

Ответ №3:

Давным-давно существовала категория NSAlert, которая использовала более старые (устаревшие) методы. Я не так уж хорош в Objective-C (и хуже в Swift), но я некоторое время использую обновленный эквивалент в одном из моих проектов RubyMotion. Надеюсь, я приблизился к отмене преобразований кода, но в основном он запускает цикл модальных событий с runModalForWindow: after calling beginSheetModalForWindow; , и обработчик завершения завершается с stopModalWithCode:

 // NSAlert SynchronousSheet.h

#import <Cocoa/Cocoa.h>

/* A category to allow NSAlerts to be run synchronously as sheets. */
@interface NSAlert (SynchronousSheet)

/* Runs the receiver modally as a sheet attached to the specified window.
   Returns a value positionally identifying the button clicked */
-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow;

/* Same as above, but runs the receiver modally as a sheet attached to the
   main window. */
-(NSInteger) runModalSheet;

@end


// NSAlert SynchronousSheet.m

#import "NSAlert SynchronousSheet.h"

@implementation NSAlert (SynchronousSheet)

-(NSInteger) runModalSheetForWindow:(NSWindow *)theWindow {
    // Bring up the sheet and wait until it completes.
    [self beginSheetModalForWindow:theWindow
                 completionHandler:^(NSModalResponse returnCode) {
        // Get the button pressed - see NSAlert's Button Return Values.
        [NSApp stopModalWithCode:returnCode];
    }];
    [NSApp runModalForWindow:self.window]  // fire up the event loop
}

-(NSInteger) runModalSheet {
    return [self runModalSheetForWindow:[NSApp mainWindow]];
}

@end
  

Приложение будет ждать завершения оповещения, прежде чем продолжить, с модальным ответом, переданным в результате. Я не знаю о блокировке пары тысяч строк кода, но я нашел это полезным при объединении двух или трех листов, например, при оповещении перед панелью открытия / сохранения.