Как правильно отображать лист "прогресс" при использовании Grand Central Dispatch для обработки чего-либо?

Я пытаюсь отобразить лист в окне, содержащем один индикатор выполнения, чтобы показать прогресс некоторой длинной функции, выполняемой асинхронно с помощью Grand Central Dispatch. Я почти понял это, но не могу заставить лист оказаться в фокусе, вероятно, потому, что я не использовал runModalForWindow: или подобное.

Это примерно то, что я делаю в данный момент, это происходит в результате нажатия кнопки в главном окне:

    // Prepare sheet and show it...

    [NSApp beginSheet:progressSheet modalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];

    [progressSheet makeKeyAndOrderFront:self];

    [progressBar setIndeterminate:NO];
    [progressBar setDoubleValue:0.f];
    [progressBar startAnimation:self];


    // Start computation using GCD...

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        for (int i = 0; i < 1000; i ++) {
            // Do some large computation here
            // ...

            // Update the progress bar which is in the sheet:
            dispatch_async(dispatch_get_main_queue(), ^{
                [progressBar setDoubleValue:(double)i];
            });
        }


        // Calculation finished, remove sheet on main thread

        dispatch_async(dispatch_get_main_queue(), ^{
            [progressBar setIndeterminate:YES];

            [NSApp endSheet:progressSheet];
            [progressSheet orderOut:self];
        });
    });

Это работает, за исключением того, что главное окно все еще находится в фокусе, лист не в фокусе, а индикатор выполнения не анимируется (если я не использую setUsesThreadedAnimation:YES на нем).

Проблема, о которой я думаю, заключается в том, что я не уверен, как запустить лист, не блокируя основной поток, прежде чем начинать асинхронное вычисление?

Ответ 1

У меня была точно такая же проблема. С некоторыми проб и ошибок я нашел решение. Убедитесь, что ваше окно листа (a) NSWindow не является NSPanel (это может не иметь значения) и что в окне есть панель заголовка (которая, поскольку она используется вами) не будет отображаться.

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

Ответ 2

Как заявил Брэд, он должен работать.

Чтобы выполнить быстрый тест, я создал лист программно (обычно, вы, вероятно, используете файл nib, но их трудно вставить в этот текст). Если я вызываю код ниже с кнопки в обычном Cocoa окне, он работает так, как ожидалось. Обратите внимание, что текстовое поле на листе является первым ответчиком, и если вы набираете клавиатуру, когда она открыта, она будет принимать вход.

#define maxloop 1000

- (IBAction)startTask:(id)sender
{
    // Prepare sheet and show it...

    breakLoop = NO;

    NSRect sheetRect = NSMakeRect(0, 0, 400, 114);

    NSWindow *progSheet = [[NSWindow alloc] initWithContentRect:sheetRect 
                                                      styleMask:NSTitledWindowMask 
                                                        backing:NSBackingStoreBuffered 
                                                          defer:YES];

    NSView *contentView = [[NSView alloc] initWithFrame:sheetRect];

    NSProgressIndicator *progInd = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(143, 74, 239, 20)];

    NSTextField *inputField = [[NSTextField alloc] initWithFrame:NSMakeRect(145, 48, 235, 22)];

    NSButton *cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(304, 12, 82, 32)];
    cancelButton.bezelStyle = NSRoundedBezelStyle;
    cancelButton.title = @"Cancel";
    cancelButton.action = @selector(cancelTask:);
    cancelButton.target = self;

    [contentView addSubview:progInd];
    [contentView addSubview:inputField];
    [contentView addSubview:cancelButton];

    [progSheet setContentView:contentView];


    [NSApp beginSheet:progSheet 
       modalForWindow:self.window 
        modalDelegate:nil 
       didEndSelector:NULL 
          contextInfo:NULL];

    [progSheet makeKeyAndOrderFront:self];

    [progInd setIndeterminate:NO];
    [progInd setDoubleValue:0.f];
    [progInd startAnimation:self];


    // Start computation using GCD...

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        for (int i = 0; i < maxloop; i++) {

            [NSThread sleepForTimeInterval:0.01];

            if (breakLoop) 
            {
                break;
            }

            // Update the progress bar which is in the sheet:
            dispatch_async(dispatch_get_main_queue(), ^{
                [progInd setDoubleValue: (double)i/maxloop * 100];
            });
        }


        // Calculation finished, remove sheet on main thread

        dispatch_async(dispatch_get_main_queue(), ^{
            [progInd setIndeterminate:YES];

            [NSApp endSheet:progSheet];
            [progSheet orderOut:self];
        });
    });
}

- (IBAction)cancelTask:(id)sender 
{
    NSLog(@"Cancelling");
    breakLoop = YES;
}

Извинения за уродливый лист, но кроме этого этот код работает так, как ожидалось, поэтому проблема, которую вы видите, вероятно, не связана с GCD.