#objective-c #cocoa #grand-central-dispatch
#objective-c #cocoa #grand-central-dispatch
Вопрос:
Я пытаюсь отобразить таблицу в окне, содержащем один индикатор выполнения, чтобы показать ход выполнения некоторой функции long, выполняемой асинхронно с использованием 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. Запуск листа модально для окна не должен блокировать основной поток (в противном случае ничего после вашего
-beginSheet:
бита в начале метода выполняться не будет). Я использую что-то почти идентичное этому (модальный лист, представленный так же, как и вы, с индикатором выполнения) в своем приложении здесь, и он отлично обновляется при запуске блока GCD в фоновом режиме. Элементы управления в окне, из которого лист скользит вниз, выделены серым цветом, указывая на то, что окно потеряло фокус на листе, так что это тоже кажется нормальным. Может ли быть что-то еще, вызывающее множество действий в основном потоке?2. @BradLarson Единственной другой вещью, о которой я мог подумать, которая могла вызывать проблемы, было
dispatch_apply
как часть основного вычисления, но замена его стандартным циклом не имела никакого значения. В противном случае в основном потоке практически ничего больше не выполняется. Я все еще могу в основном взаимодействовать с элементами управления в главном окне, за листом, и они по-прежнему отображаются в фокусе (т. Е. не выделены серым цветом). Например, во время отображения таблицы в главном окне появляется текстовое поле с кольцом фокусировки, и я все еще могу ввести в него текст (хотя я не могу выделить текст с помощью мыши).
Ответ №1:
Как заявил Брэд, это должно сработать.
Чтобы выполнить быстрый тест, я создал таблицу программно (обычно вы, вероятно, используете файл 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.
Ответ №2:
У меня была точно такая же проблема. Методом проб и ошибок я нашел решение. Убедитесь, что окно вашего листа является (а) NSWindow, а не NSPanel (это может не иметь значения) и что в окне есть строка заголовка (которая, поскольку это используемый вами лист), отображаться не будет.
По этой причине я отключил строку заголовка, но каким-то образом это необходимо для правильного достижения фокуса. Установка флажка в строке заголовка позволяет сфокусировать таблицу с индикатором выполнения.
Комментарии:
1. Да, в моем окне не было строки заголовка, ее добавление устранило проблему для меня.