Какой самый надежный способ заставить UIView перерисовать?

У меня есть UITableView со списком элементов. Выбор элемента подталкивает viewController, который затем выполняет следующее. from Метод viewDidLoad Я запускаю URLRequest для данных, которые требуются для одного из моих подпредставлений - подкласс UIView с переопределенным drawRect. Когда данные поступают из облака, я начинаю строить свою иерархию представлений. рассматриваемый подкласс получает данные, и его метод drawRect теперь имеет все, что нужно для рендеринга.

Но.

Поскольку я не вызываю drawRect явно - Cocoa-Touch обрабатывает это - я не могу сообщить Cocoa-Touch, что я действительно, действительно хочу, чтобы этот подкласс UIView отображался. Когда? Теперь было бы хорошо!

Я пробовал [myView setNeedsDisplay]. Это иногда работает. Очень пятнистый.

Я борюсь с этим часами. Может ли кто-нибудь предоставить мне надежный, гарантированный подход к принудительному повторному рендерингу UIView.

Вот фрагмент кода, который передает данные в представление:

// Create the subview
self.chromosomeBlockView = [[[ChromosomeBlockView alloc] initWithFrame:frame] autorelease];

// Set some properties
self.chromosomeBlockView.sequenceString     = self.sequenceString;
self.chromosomeBlockView.nucleotideBases    = self.nucleotideLettersDictionary;

// Insert the view in the view hierarchy
[self.containerView          addSubview:self.chromosomeBlockView];
[self.containerView bringSubviewToFront:self.chromosomeBlockView];

// A vain attempt to convince Cocoa-Touch that this view is worthy of being displayed ;-)
[self.chromosomeBlockView setNeedsDisplay];

Cheers, Дуг

108 голосов | спросил dugla 1 +04002009-10-01T16:48:47+04:00312009bEurope/MoscowThu, 01 Oct 2009 16:48:47 +0400 2009, 16:48:47

5 ответов


0

Гарантированный, надежный способ принудительного повторного рендеринга UIView: [myView setNeedsDisplay]. Если у вас возникли проблемы с этим, вы, вероятно, столкнулись с одной из следующих проблем:

  • Вы вызываете его до того, как получите данные, или ваш -drawRect: что-то перегружает.

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

Если вам нужно «немного логики, нарисовать, немного больше логики», тогда вам нужно поместить «немного больше логики» в отдельный метод и вызывать ее, используя -performSelector:withObject:afterDelay: с задержкой 0. Это добавит «немного логики» после следующего цикла отрисовки. См. этот вопрос для примера такого рода кода, и в случае, когда это может понадобиться (хотя обычно лучше искать другие решения, если это возможно, поскольку это усложняет код).

Если вы не думаете, что все прорисовывается, поставьте точку останова в -drawRect: и посмотрите, когда вас вызывают. Если вы звоните -setNeedsDisplay, но -drawRect: не вызывается в следующем цикле событий, затем копайтесь в иерархии представлений и убедитесь, что вы не пытаетесь перехитрить где-нибудь. Избыточная сообразительность является причиной № 1 плохого рисования в моем опыте. Когда вы думаете, что лучше знаете, как заставить систему делать то, что вы хотите, вы обычно получаете именно то, что вам не нужно.

ответил Rob Napier 1 +04002009-10-01T17:02:25+04:00312009bEurope/MoscowThu, 01 Oct 2009 17:02:25 +0400 2009, 17:02:25
0

У меня была проблема с большой задержкой между вызовом setNeedsDisplay и drawRect: (5 секунд). Оказалось, я вызвал setNeedsDisplay в другом потоке, чем основной поток. После перемещения этого вызова в основной поток задержка прошла.

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

ответил Werner Altewischer 26 52010vEurope/Moscow11bEurope/MoscowFri, 26 Nov 2010 13:16:35 +0300 2010, 13:16:35
0

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

В своем подклассе UIView создайте метод - display, который сообщает слою, что « да, он требует отображения », Затем« , чтобы сделать это так »:

/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
/// Not to be confused with CALayer's `- display`; naming's really up to you.
- (void)display
{
    CALayer *layer = self.layer;
    [layer setNeedsDisplay];
    [layer displayIfNeeded];
}

Также реализуйте метод - drawLayer:inContext:, который вызовет ваш частный /внутренний метод рисования (который работает, поскольку каждый UIView является CALayerDelegate)

/// Called by our CALayer when it wants us to draw
///     (in compliance with the CALayerDelegate protocol).
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
    UIGraphicsPushContext(context);
    [self internalDrawWithRect:self.bounds];
    UIGraphicsPopContext();
}

И создайте свой собственный метод - internalDrawWithRect: вместе с отказоустойчивым - drawRect::

/// Internal drawing method; naming's up to you.
- (void)internalDrawWithRect:(CGRect)rect
{
    // @FILLIN: Custom drawing code goes here.
    //  (Use `UIGraphicsGetCurrentContext()` where necessary.)
}

/// For compatibility, if something besides our display method asks for draw.
- (void)drawRect:(CGRect)rect {
    [self internalDrawWithRect:rect];
}

А теперь просто вызывайте [myView display] всякий раз, когда это действительно нужно для рисования. - display сообщит CALayer displayIfNeeded, который синхронно перезвонит в наш - drawLayer:inContext: и сделайте отрисовку в - internalDrawWithRect:, обновив визуальный элемент тем, что нарисовано в контексте, прежде чем двигаться дальше.


Этот подход похож на описанный выше @ RobNapier, но имеет преимущество вызова - displayIfNeeded в дополнение к - setNeedsDisplay, что делает его синхронным.

Это возможно, потому что CALayer предоставляет больше функциональных возможностей рисования, чем UIView s делать - слои более низкого уровня, чем представления, и разработаны специально для целей высоко настраиваемого рисования в макете и (как и многие другие объекты в Какао) предназначены для гибкого использования (в качестве родительского элемента). класс, или как делегатор, или как мост к другим системам рисования, или просто самостоятельно).

Дополнительную информацию о конфигурируемости CALayer можно найти в Настройка раздела Layer Objects ядра Core Руководство по программированию .

ответил Slipp D. Thompson 6 AMpSat, 06 Apr 2013 05:29:28 +040029Saturday 2013, 05:29:28
0

У меня была та же проблема, и все решения SO или Google не работали для меня. Обычно setNeedsDisplay работает, но когда нет ...
Я пытался вызывать setNeedsDisplay представления только всеми возможными способами из всех возможных потоков и прочего - все еще безуспешно. Мы знаем, как сказал Роб, что

  

«это должно быть нарисовано в следующем цикле рисования.»

Но по какой-то причине в этот раз это не отрисовалось. И единственное решение, которое я нашел, - это вызвать его вручную через некоторое время, чтобы пропустить все, что блокирует рисование, например:

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 
                                        (int64_t)(0.005 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
    [viewToRefresh setNeedsDisplay];
});

Это хорошее решение, если вам не нужно часто перерисовывать вид. В противном случае, если вы делаете какие-то движущиеся (действия) вещи, обычно не возникает проблем с простым вызовом setNeedsDisplay.

Я надеюсь, что это поможет тому, кто там потерян, как я.

ответил dreamzor 22 FebruaryEurope/MoscowbFri, 22 Feb 2013 19:21:02 +0400000000pmFri, 22 Feb 2013 19:21:02 +040013 2013, 19:21:02
0

Что ж, я знаю, что это может быть большим изменением или даже не подходить для вашего проекта, но не считаете ли вы, что не выполняет push, пока у вас уже нет данных ? Таким образом, вам нужно будет только нарисовать вид один раз, и пользовательский опыт также будет лучше - толчок переместится в уже загруженный.

То, как вы это делаете, находится в UITableView didSelectRowAtIndexPath вы асинхронно запрашиваете данные. Получив ответ, вы вручную выполните поиск и передаете данные в свой viewController в prepareForSegue. Между тем, вы можете захотеть показать какой-нибудь индикатор активности, для простой проверки индикатора загрузки https://github.com/jdg/MBProgressHUD

ответил Miki 5 J0000006Europe/Moscow 2013, 19:40:04

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132