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

У меня есть 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(), но он тоже не работает.


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


Ответы (6)


arrow_upward
6
arrow_downward

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

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

person drawnonward    schedule 04.05.2010
comment
Приведенный выше ответ немного вводит в заблуждение. Вы можете заставить рисование происходить вне цикла выполнения, просто вызвав [window display]. - person Leibowitzn; 11.05.2010
comment
@Liebowitzn Я думал об iPhone, который ограничивает вас пассивным рисованием. Хороший улов. - person drawnonward; 11.05.2010

arrow_upward
5
arrow_downward

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

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

Если вы нацелены на 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 вы можете захотеть прочитать это.

person wbyoung    schedule 04.05.2010

arrow_upward
1
arrow_downward

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

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

person Chris Cooper    schedule 04.05.2010

arrow_upward
1
arrow_downward

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

-(IBAction)change:(id)sender {
   [textfield setHidden:NO];
   [self performSelector:@selector(doLenghtyAction) withObject:nil afterDelay:0.1]; --> or maybe even 0.0
 }
person Mihir Mathuria    schedule 04.05.2010
comment
Это хорошее предложение, которое я бы использовал в будущем, но я пытаюсь скрыть текстовое поле, как только выполняется длинная вещь. Я попробовал ваше предложение, и оно почти сразу же снова скрывает текстовое поле, если я вставлю [текстовое поле setHidden: YES] в конце. :) - person Vervious; 04.05.2010
comment
О верно. Плевать на мой комментарий. У Горация был аналогичный ответ, который решил проблему моего первого комментария. +1 однако :D - person Vervious; 04.05.2010

arrow_upward
0
arrow_downward

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

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

arrow_upward

arrow_downward

person    schedule
comment
+1 Хороший обходной путь, хотя было бы неплохо узнать, почему оригинал не работает. - person Vervious; 04.05.2010
comment
оригинал: setHidden › doLengthyAction › setHidden не разрешает обновление пользовательского интерфейса во временном интервале. состояние текстового поля действительно изменилось дважды. однако первое изменение не обновлялось на экране. когда у пользовательского интерфейса есть возможность обновить экран, текстовое поле уже находится во втором состоянии. - person ohho; 04.05.2010
comment
Я понимаю. Так что это не моя вина, это просто свойство Cocoa. Я понимаю. :Д спасибо! - person Vervious; 04.05.2010