Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: VerviousОтветов: 5

У меня есть IBAction с простым кодом внутри:

-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    [self dolengthyaction];
}

'textfield' - это NSTextField в файле пера, а -'dolengthyaction' - это функция, выполнение которой занимает около минуты.

Мой вопрос: почему текстовое поле не отображается до тех пор, пока ПОСЛЕ выполнения "dolongyaction" не будет выполнено? Я хочу, чтобы это было раскрыто до того, как начнется долговременное действие. Это врожденная проблема или что-то не так с моим кодом? (или в другой части моего кода?)

Я все еще не очень хорош в программировании, поэтому прошу прощения, если я что-то плохо сформулировал и что-то неправильно отформатировал.

РЕДАКТИРОВАТЬ: кроме этого IBAction и -dolengthyaction больше ничего нет...

-(void)doLengthyAction {
    sleep(10);
}
-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    [self doLengthyAction];
    [textfield setHidden:YES];
}

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

По сути, это означает, что сейчас он вообще не отображается.

На самом деле в -doLengthyAction это не sleep(10), а скорее операция NSFileManager, которая копирует около 50 Мб материала. Код был довольно длинным, но если вы хотите, чтобы я его опубликовал, я могу. Я тестировал его с помощью sleep(), но он тоже не работает.

  • Я не уверен, какой ответ выбрать в качестве решения, поскольку все они объясняют это... Vervious 04.05.2010
  • Либо из ответов drawonward, либо wbyoung. Пример wbyoung тоже очень хорош, но он будет работать только на 10.6. JeremyP 04.05.2010

objective-ccocoainterface-buildernibibaction

Ответы

Ответ 1 · рейтинг 6 · drawnonward

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

Если у вас есть операция, выполнение которой занимает больше секунды, вы должны выполнять ее в потоке. После его завершения используйте PerformSelectorOnMainThread, чтобы внести изменения в пользовательский интерфейс в основном потоке.

Ответ 2 · рейтинг 5 · wbyoung

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

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

Если вы нацелены на 10.6+, вы можете использовать GCD и блоки следующим образом, чтобы легко запускать вещи в фоновом режиме (и вам не нужно определять несколько методов для выполнения задач):

-(void)doLengthyAction {
    sleep(10);
}
-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self doLengthyAction];
        dispatch_async(dispatch_get_main_queue(), ^{
            [textfield setHidden:YES];
        });
    });
}

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

Ответ 3 · рейтинг 1 · Chris Cooper

Я думаю, что что-то не так с остальной частью вашего кода. Этого не должно происходить.

Опубликовать больше?

Ответ 4 · рейтинг 1 · Mihir Mathuria

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

-(IBAction)change:(id)sender {
   [textfield setHidden:NO];
   [self performSelector:@selector(doLenghtyAction) withObject:nil afterDelay:0.1]; --> or maybe even 0.0
 }

Ответ 5 · Leibowitzn

Попробуй это:

-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    [[textfield window] display]; // force the window to update
    [self doLengthyAction];
    [textfield setHidden:YES];
}

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы