Перерисовка UIControls/UIButtons на iPhone в примере кода BubbleLevel

Я не понимаю, когда мне следует вызывать -setNeedsDisplay. Например, для обновления кнопки разными изображениями между нажатиями (переключение между ними) пример кода от Apple показывает, что -setImage вызывается для UIButton. Однако после этого я не могу найти вызов -setNeedsDisplay. Итак, все ли методы UIControl выполняют -setNeedsDisplay внутри, и, следовательно, -setNeedsDisplay следует использовать только при манипулировании экземплярами UIView? Или есть более конкретная семантика? Если я создаю подкласс UIView, должны ли мои методы всегда обрабатывать внутреннюю перерисовку? Как/где это указано в документах?

Из LevelView.m в исходном коде BubbleLevel:

- (void)toggleHoldButton:(id)sender {
    if (holdButtonIsShowing == YES) 
    {
        holdButtonIsShowing = NO;
        [holdButton setImage:[UIImage imageNamed:@"release_button.png"] forState:UIControlStateNormal];
    } else 
    {
        holdButtonIsShowing = YES;
        [holdButton setImage:[UIImage imageNamed:@"hold_button.png"] forState:UIControlStateNormal];
    }    
}

person snackdefend    schedule 08.09.2010    source источник


Ответы (1)


setNeedsDisplay используется для повторного рендеринга или повторного рисования представления (экземпляр подкласса UIView или UIView). С нашей точки зрения: он вызывает drawRect:, но на самом деле перед этим подготавливает контекст и делает вид готовым к перерисовке. (Вот почему вам не следует вызывать drawRect: напрямую)

Обычный случай, когда вы реализовали метод drawRect: (некоторые примеры: отрисовка содержимого с помощью OpenGL в представлении или отрисовка текста с использованием CoreText или отрисовка некоторой графики с использованием CoreGraphics и т. д.), если вы хотите обновить рисунок в определенный момент, вы явно вызываете setNeedsDisplay, и он вызовет функцию drawRect:, и ваш вид сразу же будет перерисован. Если нет, метод drawRect: будет вызываться при изменении границ представления и в некоторых других ситуациях, когда система решит, что необходимо перерисовать представление.

В случае кнопки вам не нужно этого делать, потому что, конечно, когда кнопка нажата, будет выполнен внутренний вызов setNeedsDisplay, заставляющий кнопку измениться и т. д. (Кто действительно знает, что происходит в фоновом режиме. Дело вам не нужно беспокоиться о drawRect: при использовании UIButtons таким образом)

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

Хорошим правилом может быть то, что вам не нужно явно вызывать setNeedsDisplay, если вы не реализовали метод drawRect:.

http://developer.apple.com/iphone/library/documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/drawRect:

Надеюсь, поможет.

;)

ОБНОВЛЕНИЕ: 09.09.2009

то, как UIButton вызывает drawRect: зависит от его реализации, и мы не должны заботиться об этом (по крайней мере, если мы не создаем подклассы). Но если вы действительно это знаете, вы можете создать подкласс и переопределить:

- (void) drawRect:(CGRect)frame{
    NSLog(@"%s is being called!!!", _cmd);
    [super drawRect:frame];
}

а теперь из другого объекта попробуйте загрузить его, изменить его размер, переместить, прокрутить (если доступно), скрыть его и увидеть консоль отладки (Cmd + Shift + R). Я бы рекомендовал сделать это не только для UIButton, но и для обычный UIView. Это наверняка даст четкое представление о том, когда вызывается drawRect:. ;) (Также другие методы, такие как layoutSubviews: и т. д.)

Причина, по которой я сказал, что мы не должны заботиться о drawRect: в UIButton, заключается в том, что он нам не нужен, мы устанавливаем изображение для определенного состояния, используя setImage: forState: и все. Мы можем переустановить эти изображения в любое время, и кнопка изменит свое изображение по мере необходимости, и она может перерисовать свой вид в зависимости от того, как ее разработали инженеры Apple.

Что касается вашего вопроса, соответствует ли setNeedsDisplay правилу, которое я вам дал. Я верю, что да. Я поставил точку останова в начале каждого метода и ничего и проверил, что происходит, когда вызывается setNeedsDisplay. Ни один из методов LeverView не вызывался!. затем я прокомментировал 3 вызова setNeedsDisplay, и результаты были такими же! (iOS4.1) Следовательно, эти три строки кода не нужны. ;)

Вы должны быть осторожны при вызове setNeedsDisplay, потому что в зависимости от вашей реализации это может сделать вашу программу очень медленной! так как собирается все перерисовывать и наверное не надо. ;)

Фууу... лол. Довольно длинный пост только про drawRect:

Ваше здоровье

person nacho4d    schedule 08.09.2010
comment
Большое спасибо. Однако для моего собственного назидания, если, например, я попытаюсь воссоздать класс UIButton, мне все еще любопытно, когда именно происходит drawRect:. Это 1) Вызывается целевое действие (где элементам кнопки можно было бы сказать обновить, например, -setImage :), а затем управление возвращается вызывающему, который выполняет один setNeedsDisplay? Или 2) каждое сообщение, такое как setImage: отправленное в сами объекты UIButton, связано с повторным отображением? Кроме того, для этого эмпирического правила включает ли он вызов setNeedsDisplay в том же примере внутри setupSubviewsWithContentFrame:? - person snackdefend; 08.09.2010
comment
Большое спасибо, эта идея создания подклассов великолепна, попробуйте ее сейчас. Я ценю разъяснение setNeedsDisplay; У меня были те же результаты после их комментирования. Я предполагаю, что вызов внутри инициализации LevelView просто надеется, что планировщик начнет рисовать как можно скорее после запуска приложения. - person snackdefend; 14.09.2010