views:

1747

answers:

5

I am producing an iPhone app for which part of the interface is exactly like the 'Most Popular' section of the iPhone YouTube app.

This 'popular' section is accessed from a Tab Bar at the bottom and the navigation bar at the top contains a UISegmentedControl to select 'Today, This Week, Month etc..'

Because most of the app consists of UITableViews with cells containing very similarly structured content, I have created a common MyAppTableViewController which inherits UITableViewController. My 'popular' section thus consists of a PopularTableViewController which inherits MyAppTableViewController. The actual UITableView resides within MyAppTableViewController.

PopularTableViewController has the method:

- (void) segmentChangeTimeframe:(id)sender {
    UISegmentedControl *segCtl = sender;
    if( [segCtl selectedSegmentIndex] == 0 )
    {
        // Call [self parse-xml-method-which-resides-in-MyAppTableViewController]
    }
    //... ... ...
}

The MyAppTableViewController makes use of NSXMLParser and thus has the code:

- (void)parserDidEndDocument:(NSXMLParser *)parser {
    [self.myTableView reloadData];
}

(There are other methods which updates the data structure from which the table view gets it's data)

I have put console output code into the xml parsing methods, and when run, selecting the different segments causes the correct xml files to be parsed fine and the data structure seems to contain the correct values.

The problem is that the contents of the table cells wont change! grr! UNLESS!... A cell is scrolled out of view, and then back into view... THEN its changed!

I have done lots of searching about for this problem and one suggestion for a similar problem was to place the [self.myTableView reloadData] into its own method e.g. myReloadDataMethod and then use:

[self performSelectorOnMainThread:@selector(myReloadDataMethod) withObject:nil waitUntilDone:NO];

I tried placing the above code into the parserDidEndDocument method and it made absolutely no difference! I'm absolutely stumped and am wondering if anybody has any idea what's going on here.

Update:

The code to populate the cells is done with:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *MyIdentifier = @"MyIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:MyIdentifier] autorelease];
    }

    // Set up the cell
    int itemIndex = [indexPath indexAtPosition: [indexPath length] - 1];
    NSString *artistName = [[myItemList objectAtIndex: itemIndex] objectForKey: @"itemA"];
    NSString *mixName = [[myItemList objectAtIndex: itemIndex] objectForKey: @"itemB"];
    cell.textLabel.text = itemA;
    cell.detailTextLabel.text = itemB;
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;

    return cell;
}

The above code is in MyAppTableViewController which is also where myItemList resides.

+2  A: 

Your -performSelectorOnMainThread: code is for when you make changes to the model classes on a background thread. UI events (including -reloadData) need to occur on the main thread. If you're not using a background thread, then this is unnecessary. If you are, something like it is mandatory.

If you are changing the value of a specific cell, the way you achieve that is to change the cell itself. On iPhone, cells are full views (unlike on Mac), so if you want to change their data, you just change their data and call -setNeedsDisplay. You can get the cell (view) for a given location using -cellForRowAtIndexPath:. You can determine if a given cell is onscreen by using -indexPathsForVisibleRows or -visibleCells.

It is very rare to need to call -reloadData. You should only do that if you are throwing away everything and loading completely different data. Instead, you should use the insertion/deletion routines to add/remove rows, and you should just update the views of existing rows when their data change.

Rob Napier
Hi Rob. Thanks for your answer. Each selected segment index causes a different xml file to be parsed (for 'Today'/'Week'/'Month') and it's just the content of that selected xml I want displayed rather than it being added to the existing cells. Should I not be using reloadData in this case?
IAmFledge
That should be -reloadData. Are you parsing on the main thread, or are you changing the model data in the background?
Rob Napier
As I'm pretty new to iPhone/ObjC programming I'm not entirely sure how the iPhone OS handles threads. (e.g. I don't know whether or not connections you make in the interface builder cause new threads to be kicked off behind the scenes). But I have not explicitly declared a new thread. I just have my AppDelegate which contains my UITabBarController *tabBarController (for which I have no code, it's just connected up in the I.B.) and my controller classes as described in the question.In the I.B. my TabBarController has ownership of some NavigationControllers, each having ownership of...
IAmFledge
...the relevant custom UITableViewController that inherits MyAppTableViewController. (Sorry, ran out of characters on last post!).
IAmFledge
It's not threading if you didn't set it up that way. Bah... I misread the question. I thought you were saying that the view was jumping around (scrolling itself). It just doesn't know that it needs to redraw... -reloadData should do that, but if it isn't, then try adding a call to -setNeedsDisplay on the NSTableView. Make sure that you're calling reloadData (or setNeedsDisplay) *after* you've updated whatever data structures you use in your NSTableView datasource (the one that returns numberOfRows and the cells).
Rob Napier
When you say NSTableView, do you mean UITableView? I tried adding [self.myUITableView setNeedsDisplay] after the reloadData call and it falls over with NSInvalidArgumentException - unrecognized selector sent to instance. Am I being a tool and doing something really stupid?
IAmFledge
Sorry, this is iPhone... Yes, UITableview. UITableView is a type of UIView, and responds to setNeedsDisplay. Do you have a typo in the "myUITableView?" Earlier the property was "myTableView". (If that was a typo in the code rather than here, make sure you always heed all compiler warnings.)
Rob Napier
Oops sorry..I should have mentioned that my client requested that code is anonymised for public forums, so they're placeholder variable names.In the actual code-code, it's spelt consistently. I've been making use of XCode's auto-complete to ensure that doesn't happen (and of course to type faster ;)) I have no compiler warnings. This problem is infuriating.
IAmFledge
If you're getting that error, dig deeper into it. UITableView definitely should respond to setNeedsDisplay. http://developer.apple.com/iPhone/library/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/setNeedsDisplay
Rob Napier
Hi Rob, the error was being caused by something else (although for some reason the compiler was telling me it was at the new setNeedsDisplay line..hmm!)Anyway, setNeedsDisplay isn't causing it to fall over, but adding it seems to make no difference. I've tried putting both reloadData and setNeedsDisplay in lots of different locations including right at the end of my 'if ([segCtl selectedSegmentIndex] == 0 ){...}' block so it should be the absolute last thing that's called after clicking the segment! But it's still the same old problem of the cells not updating till they're off screen.
IAmFledge
I have also tried putting [cell setNeedsDisplay] just before return cell; in my cellForRowAtIndexPath:(NSIndexPath *)indexPath {...} block and alas no luck.
IAmFledge
Is your tableview instantiated by a NIB? Make sure that myTableView is actually wired to anything (make sure it isn't nil). When "nothing happens", the most common cause is that you're messaging nil. If the NIB instantiates the tableview, then it will still mostly work even if the IBOutlet isn't wired.
Rob Napier
A: 

I myself have been getting the same problem and its really ticking me off that i cant figure it out, what i did was call [myTableView reloadData]; in the parserDidEndDocument method in my parser classi would really appreciteate it if someone can give us the answer cause then i going to have to call performMethodInBackground method and have to make the program sleep for 4 seconds, which works it updates the diaplay with all the new cells but it makes my program very inefficant, but i dont want to do this since this is pratically what each view in my program will do update data in a tableView from a xmlDocument

charles Graffeo
A: 

I've same sort of typical table reload problem. I've a tabbar controller associated with the navigation controller. I've separate tableview controllers for two tabs. While the first tab - table reload works fine, in the second tab, table view reference is turning out to be nil. (Memory reference is coming as 0x0). Any help in this regard will be greatly appreciated.

MM
A: 

Have you tried [tableView setNeedsDisplay:YES]?

Frank Schmitt
A: 

I had this same problem, and it was because I had a [tableView beginUpdates] call without an endUpdates call after.

boxed