views:

195

answers:

2

I have something weird with the repaint of my view controller. The view controller contains an UITableView and a spinner.

I have an updateFeed function (fired by an IBOutlet) who brings the spinner in front of my view controller and puts a doUpdate function into an NSOperationQueue.

- (void)updateFeed {
    [self showMuar];
    ResourceLoader *loader = [ResourceLoader sharedResourceLoader];
    [loader updateFeed:[self feed] forDelegate:self];
    isReloading = YES;
}
- (id<ResourceToken>)updateFeed:(Feed *)feed forDelegate:(id)delegate {
     return [self queueOperation:[[Operation alloc] initWithObject:feed withSelector:@selector(doUpdate) delegate:delegate]];
}

The doUpdate function calls a parser (startParserWithAdd:) function who retrieves content from internet and format this content for the UITableView and then perform a [tableView reloadData].

- (NSError *)doUpdate {
    [self startParserWithAddThreaded:NO];
    return nil;
}
- (void)startParserWithAddThreaded:(BOOL)add {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [self startParserWithAdd:add];
    [pool release];
}

... somewhere in the parser function (startParserWithAdd:)

[tableView setDataSource:self];
[tableView reloadData];

Once the doUpdate done without errors and thanks to my Operation class the loadingResourceDidFinish is performed in my view controller. loadingResourceDidFinish send the spinner in background.

- (void)loadingResourceDidFinish:(Feed*)f {
    isReloading = NO;
    [self loadingResourceDidFinishPostAction];
}
- (void)loadingResourceDidFinishPostAction {
        [self hideMuar];
}
- (void)hideMuar {
    [loadingIndicator stopAnimating];
    [self.view sendSubviewToBack:muar];
}

Everything works fine but the spinner is sent to background about 4s after the loadingResourceDidFinish is finished.

To be exact, muar is an UIView with opacity who contains the spinner. I just wanted to abstract complexity in my original explaination !

I tried to NSLog this and it sounds like nothing is repainted until the reloadData is complete. I think it's a repaint problem because if i turn my iPhone in landscape just after the end of loadingResourceDidFinish, everything is well, the spinner disapears.

I tried to remove the NSAutoreleasePool in the parser function but doesn't change.

I surely misunderstood something but i don't know what it is ...


edit

I changed the code to make it less confusing. The reloadData of the UITableView is now placed here :

- (void)loadingResourceDidFinishPostAction {
    [tableView reloadData];
    [self hideMuar];
    NSLog(@"loadingResourceDidFinishPostAction end");
}

And for debug :

- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"cellForRowAtIndexPath %i", indexPath.row);
        ...

So why the first cell is repainted only 5s after the end of loadingResourceDidFinishPostAction ?

2010-09-02 10:15:35.279 myapp[9632:5b03] loadingResourceDidFinishPostAction end
2010-09-02 10:15:40.208 myapp[9632:5b03] cellForRowAtIndexPath 0
2010-09-02 10:15:40.697 myapp[9632:5b03] cellForRowAtIndexPath 1
2010-09-02 10:15:40.878 myapp[9632:5b03] cellForRowAtIndexPath 2

It is the same thing with my iPhone 3G and the iPhone 4 simulator.

A: 

Here is my new answer for your editted question.

I don't know exactly what happens because usually, the table view will reload data and redraw the table view in separate thread that will not run that slow. Here is some guess that you can check:

1/ Check if you reuse the cell by cell identifier correctly

2/ Check if any other data source and delegate methods do heavy, networking job like in

* – tableView:cellForRowAtIndexPath:  required method
* – numberOfSectionsInTableView:
* – tableView:numberOfRowsInSection:  required method

or in:

#  – tableView:heightForRowAtIndexPath:
# – tableView:indentationLevelForRowAtIndexPath:
# – tableView:willDisplayCell:forRowAtIndexPath:
vodkhang
Hi, i already tried this and play with [muar setHidden:YES] too but it doesn't work
lefakir
I don't see your call to [self.tableView reloadData]?
vodkhang
I added few lines of code to illustrate this
lefakir
+1  A: 

I'm not sure what's causing your delay, but it smells like it could be a threading issue. Many UIKit classes aren't thread safe. As a general rule, you should try to make sure that all of your UI interaction happens on the main thread. In the code you originally posted, it looked like you were calling reloadData from your background thread, which seems risky. It's not clear to me which thread it's being called from now that you've changed your code.

Go through your doUpdate code path and make sure that any calls back to your delegate that could lead to a UI update are being done via performSelectorOnMainThread:.

cduhn
thank you very much, you made my day !i checked this and finally changed :- (void)loadingResourceDidFinishPostAction { [tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES]; [self performSelectorOnMainThread:@selector(hideMuar) withObject:nil waitUntilDone:YES];}
lefakir