views:

2026

answers:

1

I have a UITableView with a list of items. Selecting an item pushes a viewController that then proceeds to do the following. from method viewDidLoad I fire off a URLRequest for data that is required by on of my subviews - a UIView subclass with drawRect overridden. When the data arrives from the cloud I start building my view hierarchy. the subclass in question gets passed the data and it's drawRect method now has everything it needs to render.

But.

Because I don't call drawRect explicitly - Cocoa-Touch handles that - I have no way of informing Cocoa-Touch that I really, really want this UIView subclass to render. When? Now would be good!

I've tried [myView setNeedsDisplay]. This kinda works sometimes. Very spotty.

I've be wrestling with this for hours and hours. Could someone who please provide me with a rock solid, guaranteed approach to forcing a UIView re-render.

Here is the snippet of code that feeds data to the view:

// 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, Doug

+5  A: 

The guaranteed, rock solid way to force a UIView to re-render is [myView setNeedsDisplay]. If you're having trouble with that, you're likely running into one of these issues:

  • You're calling it before you actually have the data, or your -drawRect: is over-caching something.

  • You're expecting the view to draw at the moment you call this method. There is intentionally no way to demand "draw right now this very second" using the Cocoa drawing system. That would disrupt the entire view compositing system, trash performance and likely create all kinds of artifacting. There are only ways to say "this needs to be drawn in the next draw cycle."

If what you need is "some logic, draw, some more logic," then you need to put the "some more logic" in a separate method and invoke it using -performSelector:withObject:afterDelay: with a delay of 0. That will put "some more logic" after the next draw cycle. See this question for an example of that kind of code, and a case where it might be needed (though it's usually best to look for other solutions if possible since it complicates the code).

If you don't think things are getting drawn, put a breakpoint in -drawRect: and see when you're getting called. If you're calling -setNeedsDisplay, but -drawRect: isn't getting called in the next event loop, then dig into your view hierarchy and make sure you're not trying to outsmart is somewhere. Over-cleverness is the #1 cause of bad drawing in my experience. When you think you know best how to trick the system into doing what you want, you usually get it doing exactly what you don't want.

Rob Napier
Rob,Here's my checklist.1) Do I have the data? Yes. I create the view in a method - hideSpinner - called on the main thread from the connectionDidFinishLoading: thusly:[self performSelectorOnMainThread:@selector(hideSpinner) withObject:nil waitUntilDone:NO];2) I don't need drawing instantly. I just need it. Today. Currently, it is completely random, and out of my control. [myView setNeedsDisplay] is completely unreliable.I've even gone so far as to call [myView setNeedsDisplay] in viewDidAppear:. Nuthin'. Cocoa simply ignores me.Maddening!!
dugla
Looking at your code, the first thing you want to make sure of is that contatinerView is itself on the screen. Put something else in it (a UILabel for instance) and see if that draws. Second, make sure that self.containerView is not nil. A primary cause of "nothing happens" is sending a message to nil. You should not need a setNeedsDisplay here, but if you did it would be more typical to send it to containerView rather than chromosomeBlockView. You're asking the containerView to redraw itself and its subviews (which you have rearranged), one of which happens to be chromosomeBlockView.
Rob Napier