Установите UIView backgroundColor с помощью drawRect:

Я пытаюсь установить цвет фона пользовательского класса UIView. Класс также рисует кварц по методу drawRect:.

Поскольку изменение цвета фона не происходит до следующей перерисовки представления, я изменяю свойство backgroundColor UIView перед вызовом setNeedsDisplay. Я установил UIActivityIndicatorView для анимации во время перерисовки вида.

self.backgroundColor = theColor; 
[indicator startAnimating];
[self performSelectorInBackground:@selector(setNeedsDisplay) withObject:nil];

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

Допустим, у меня есть трудоемкий setNeedsDisplay процесс. Я хотел бы установить фон и сохранить анимацию индикатора. В настоящее время изменение backgroundColor вызывает setNeedsDisplay, но даже не изменяет backgroundColor, пока не запустится метод performSelectorInBackground! Поэтому мое приложение зависает, и индикатор никогда не анимируется. Как решить эту проблему с заказом? Спасибо.

Изменить: я имел в виду, что мой drawrect: может занять много времени.


person ansonl    schedule 24.02.2013    source источник
comment
setNeedsDisplay не занимает много времени, это drawRect:. но вы все равно не должны звонить drawRect:.   -  person Bryan Chen    schedule 24.02.2013


Ответы (2)


Допустим, у меня есть трудоемкий процесс setNeedsDisplay.

Давайте не будем. Вы не имеете права переопределять setNeedsDisplay. Мне совсем не ясно, чего вы в конечном итоге пытаетесь достичь, но весь этот вопрос кажется неправильным пониманием того, как рисовать. Когда вы вызываете setNeedsDisplay (что, как вам сказали, вы должны делать в основном потоке), это все; вы стоите в стороне, и когда наступает момент перерисовки, вызывается drawRect: вашего представления. Это рисование.

Если проблема просто в том, что индикатор активности никогда не запускается, это потому, что вы никогда не даете ему шанса. Он тоже не собирается начинать движение до момента перерисовки. Но вы останавливаете индикатор активности еще до того, как наступает момент перерисовки! Так что, очевидно, вы никогда не увидите, как это происходит.

Чтобы сделать индикатор активности заметным до того, как вы сделаете следующее, нужно выйти в основной поток после следующего момента перерисовки. Это называется «задержка исполнения». Пример:

self.backgroundColor = theColor; 
[indicator startAnimating];
double delayInSeconds = 0.1;
dispatch_time_t popTime =
    dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    // do something further, e.g. call setNeedsDisplay
};

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

Однако я должен убедить вас, что если сам процесс рисования занимает так много времени, что вам нужен индикатор активности, чтобы покрыть его, вы рисуете неправильно. Ваш акт рисования должен быть очень-очень быстрым. Вы можете посмотреть видео WWDC 2012 на эту тему; он дает отличные советы о том, как эффективно рисовать.

person matt    schedule 24.02.2013
comment
Извините за путаницу, я имел в виду, что мой метод drawrect: может занять некоторое время. Сложность рисунка зависит от пользовательского ввода, поэтому я стараюсь подготовиться к худшему. Dispatch также не поддерживается в ‹ ios 4.3, насколько я помню. Должен ли я не использовать PerformSelector? Индикатор работает нормально, если я не включаю настройку фона. - person ansonl; 24.02.2013
comment
@AnsonL, если ваш метод drawrect: требует времени, вам нужно его исключить. Как сказал Мэтт, на эту тему есть отличное видео WWDC2012 (правда, в данном случае речь идет о многопоточности, а не о рисовании). Что вам нужно сделать, так это поместить работу по рисованию в отдельный поток (работать с UIImage вместо фактического представления?), а затем, когда он будет завершен, перенести данные в основной поток для обновления пользовательского интерфейса. - person RonLugge; 24.02.2013
comment
@AnsonL Перечитай, что я сказал: не заставляй меня повторяться. Ваше drawRect: должно не занять некоторое время. Вы должны рисовать быстро. Вам не нужна диспетчеризация для отложенной производительности; если вы не понимаете отложенную производительность или многопоточность, вы можете прочитать мою книгу: apeth.com /iOSBook/ch38.html, apeth.com/iOSBook/ch11.html #_delayed_performance. Прежде всего, посмотрите эти видео WWDC; Я имею в виду, в частности, сессии 235 и 238 WWDC 2012. - person matt; 24.02.2013
comment
@matt Хорошо, я просмотрел документы GCD и вашу книгу. В своей книге вы используете dispatch_after с задержкой 0,1. Вы говорите, что это нужно для того, чтобы индикатор показал время. Но нельзя ли также избавиться от искусственной задержки 0,1, используя dispatch_async с пользовательским dispatch_queue_t, чтобы избежать засорения main_queue? - person ansonl; 03.03.2013
comment
Я не засоряю main_queue. Я делаю прямо противоположное: я стою в стороне, пока основная очередь не закончит делать то, что она делает (т.е. я жду завершения текущего цикла выполнения). - person matt; 03.03.2013

Вы можете обновить пользовательский интерфейс только в основном потоке, а не в фоновом режиме.

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

person iiFreeman    schedule 24.02.2013