Как правильно отображать «прогресс» лист модально при использовании Grand Central Dispatch для обработки чего-либо? - PullRequest
11 голосов
/ 08 ноября 2011

Я пытаюсь отобразить лист в окне, содержащем один индикатор выполнения, чтобы показать ход выполнения некоторой длинной функции, выполняемой асинхронно с использованием 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 на нем).

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

Ответы [ 2 ]

4 голосов
/ 16 ноября 2011

Как сказал Брэд, это должно работать.

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

#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.

2 голосов
/ 13 февраля 2013

У меня была точно такая же проблема.С помощью проб и ошибок я нашел решение.Убедитесь, что окно вашего листа (а) является NSWindow, а не NSPanel (это может не иметь значения), и что окно имеет строку заголовка (которая, поскольку это лист, который вы используете) не будет отображаться.* По этой причине я отключил строку заголовка, но каким-то образом это необходимо для правильной фокусировки.Отметив флажок в строке заголовка, я получаю фокус листа индикатора выполнения.

...