Loading cells via nib and referencing components in them - iphone

I'm loading a UITableViewCell that contains two tagged labels. The user will leave the current view, containing the following code and go to another view. A name value is set there and the user comes back to this view (code below). I assign the name they set in the other view to the name label. That works but I get a new label misaligned on top of my other labels. It seems I'm keep two versions of this cell. Not quite sure. Any suggestions on what might be causing that sort of behavior?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if(indexPath.section == 0)
{
cell = [tableView dequeueReusableCellWithIdentifier:#"CellNameIdentifier"];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CellentName" owner:self options:nil];
cell = cellName;
//self.cellName = nil;
}
}
return cell;
}
- (void)viewDidAppear:(BOOL)animated {
UILabel *startdate = (UILabel *)[cellName viewWithTag:1];
startdate.text = aName;
[super viewDidAppear:animated];
}

From the look of this code it is less likely that you are getting "a new label misaligned on top of my other labels" and more like the drawing is failing to repaint on top of things properly. To make this work, you can try calling [tableView reloadData] or using an observer, but I think there is a better way.
You should be able to pass the object into your other view, modify the object (instead of the label) and move the data around that way. In other words on the table view, it loads the objects, and inside of cellForRowAtIndexPath it loads the cells and sets the label names using the object data. Push in the second view and pass the object as a property. Manipulate this property all you want on that screen and when you pop the view, there is no special logic. The first table view again displays whatever is saved inside that object you were manipulating.

Related

How to display a complex UIViewController inside another UIView Controller?

Ok, so here's the situation. I currently have a view controller called MainViewController which has a UITableView with many different cells. When I click on a cell, I want that cell to expand (grow in height) and show some "additional information". The problem is, this additional information is very complex and can contain UILabels, other UITableViews, UIWebViews and UIImageViews. Furthermore, this "additional data" requires quite a bit of computation in order to determine what exactly to display (i.e. what the UILabels say, how large the UIImageViews are). Therefore, because of the complexity of this "additional information", I'm at a loss as to how to design my program.
The "additional information" requires a lot of code, thus I don't want to just throw that code into the MainViewController. Additionally, it would be nice if there was some way to use Interface Builder to design these "additional information" views graphically rather than programatically.
Currently I have each set of additional information as its own separate UIViewController (thus allowing me to have separate classes for the data and allowing me to use interface builder) and I just segue to a new screen when a cell is selected. However, I don't want to segue to a new screen; I want all of the data that this UIView controller is showing to be shown in MainViewController. What's the best way to do this?
In summary, I currently have one UIViewController segueing to another UIViewController; however, I want the second UIViewController's content to be show in the first. If possible I would like to use some sort of Interface Builder and to separate out the logic for this second UIViewController into another class.
Details:
~ I'm developing for iOS 5 only and I'm using ARC.
~ I've never developed for iOS 4 or below before and I have never used nib files before but I would be willing to learn if required. Simple sample code would be helpful.
~ Thanks!
Same opinion as SmartWork.
You should create your custom UITableViewCell class with its xib file, with a UITableViewCell as main xib view
And in your tableView datasource, you can import it as below :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MyViewCell";
MyViewCell *cell = (MyViewCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyViewCell" owner:nil options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (MyViewCell *)currentObject;
break;
}
}
}
[self configureCell:cell atIndexPath:indexPath]; // your own function to customize your cell.
return cell;
}
Then, in the cell xib, you can set the max height of the cell, and decide the effective height in the UITableViewDelegate class.
There are some good suggestions here, but note that loadNibNamed:owner: is a fairly expensive API to call repeatedly because it reads the nib from the filesystem each time. Instead, consider doing something like this.
Register your nib file in viewDidLoad. In Interface Builder, make sure to provide a reuse identifier for your custom cell.
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *myNib = [UINib nibWithNibName:#"MyNib" bundle:nil];
[self.tableView registerNib:myNib forCellReuseIdentifier:#"MyCell"];
// Do any other stuff you need to do...
}
Then just dequeue your custom cell whenever you need it. UINib will cache the nib file in memory to avoid reloading it from the filesystem each time.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
// Configure cell as needed...
return cell;
}
in my opinion there is no need of using one UiviewController inside another.
u can use Uiview with nib file so u can design these "additional information" views graphically. its very easy to implement and maintain it.
Just to add to what SmartWork said, when you tap a particular cell, you can update the height of that row using the following lines of code:
- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// set a dynamic value for the cell height depending on the state of the data in the cell
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// update the state of the data in the cells here..
// calling these below lines will change the height of the cells smoothly
[tableView beginUpdates];
[tableView endUpdates];
}
You will also need custom UITableViewCells. Look at them as simple views and add and remove any number of subviews that you need
If you are keen on using Nibs for your subviews inside the cells, you can create their nibs and connect them to your Custom TableView Cells as follows: (The subviews can be properties of your tableViewCell)
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"MyTableViewCellNib" owner:self options:nil];
mySubview = [(SubView *)[nib objectAtIndex:0]retain];

UITableViewCell from a custom .XIB file doesn't create outlets

Update 2:
Long story short, I was being silly (and, in my defense, not properly educated) about this. It works now. My questions have derailed from the original topic a little bit, but that's because I wasn't understanding what was going on in my application. I would like to close the question with one last (and small) query:
I've got two labels in my customCell.xib. I want one of them (cell.label2) to sometimes contain a longer segment of text (2-3 lines). I know one way to make it all fit is to set the autoshrink property, but that shrinks to the text so it can fit on a single line. I want to preserve the original text size and expand the cell's height instead, making the text span multiple lines instead of shrinking it.
Is there a way to do this?
Update 1:
I tried a few things based on the replies below, and they got me nowhere. I am growing convinced that I am doing something fundamentally wrong, and you guys just can't think of it because it's so basic. So let's try again. I am going to changed the names a little, so they are easier to remember.
The problem seems to be in the fact that I can't create any IBOutlets or IBActions for my customCell. Right now I have 3 files that should handle this (DetailedDirectionViewCell.{h,m,xib}, but the Interface Builder doesn't allow me to create a property/outlet/reference out of my UITableViewCell object - anywhere.
Instead of copying the code here, I've provided a PasteBin entry with links to my code. As before, I've removed the less interesting methods. Take a look if you will.
http://pastebin.com/p4eADhKQ
I also have customCell.{h,m}, but those are just new Objective C class files that inherit from UITableViewCell. customCell.xib is just a cell with two labels.
So I have a couple of problems really.
First, generating a UITableView programmatically, using a custom UITableViewCell contained in a .XIB file of its own. My DirectionsViewController class is just a UITableView with programmatic cells. Tapping on one of the cells needs to present a DetailedDirectionsViewController table (in a modal way), the cell design for which sits in a DetailedDirectionsViewCell.xib file. The problem is, I can't create an IBOutlet for the UITableViewCell from the nib file - anywhere. Dragging the File's Owner icon/outlet doesn't offer to create anything. Which, after 5 hours of struggling, means that I can't present my detailed view.
The second problem involves adding a navigation bar to the DirectionsViewController, but let's leave that alone for now.
Here are some methods you might find helpful:
//inside DirectionsViewController
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)
{
DetailedDirectionsViewController *vc = [[DetailedDirectionsViewController alloc] initWithStyle:UITableViewStyleGrouped];
vc.instruction = [turnList objectAtIndexPath:indexPath.row];
[self.tabBarController presentModalViewController:vc animated:YES];
}
//inside DetailedDirectionsViewController
- (UITableViewCell *) tableView:(UITableView *) cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"directionsCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
[[NSBundle mainBundle] initWIthNibNamed:#"DetailedDirectionsViewCell" owner:nil options:nil];
cell = self.tableViewCell;
self.tableViewCell = nil;
}
//here I configure the custom cell
return cell;
}
I don't think the rest of the methods are of interest, because they are either working as expected, or are pretty much the default ones.
To sum up:
DirectionsViewController - essentially a UITableViewController with custom cells. No .xib file
DetailedDirectionsViewController - detailed information about the entries from DirectionsViewController. Cells here should come from a .XIB file, but that's broken.
DetailedDirectionsViewCell - this is the custom cell. I can't set its File's Owner.
Ok.. You do not create IBOutlet connection from File's Owner. Have a look at a screenshot. You create IBOutlet from CustomCell's view(with Red Arrow).
Looking after your code just follow these steps.
1) Goto CustomCell.h file. As you are saying customCell.xib has two UILabels(assume label1 & label2) you gonna have to declare properties and create outlets in CustomCell.h file and in .m file synthesize and release it. Refer this code screen of mine.
2) Now in CustomCell.xib, select view of CustomCell not File's Owner(File's Owner should inherit from NSObjectonly) go to Identity Inspector(Marked with Red Ellipse) and select the corresponding Customcell class (marked with Red rectangle).
3) Right click your customcell's view and make connections to labels. And save it..
4) In your DirectionsViewController.m you have this UITableView's delegate method cellForRowAtIndexPath. Change it like this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CustomCellIdentifier = #"CustomCell";
EditProjectCustomCell *cell = (EditProjectCustomCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier]; // typecast to customcell
[cell setSelectionStyle:UITableViewCellSelectionStyleBlue];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"EditProjectCustomCell" owner:self options:nil];
for (id oneObject in nib)
if ([oneObject isKindOfClass:[EditProjectCustomCell class]])
cell = (EditProjectCustomCell *)oneObject;
[cell setSelectionStyle:UITableViewCellSelectionStyleBlue];
}
cell.label1.text=[someArray1 ObjectAtIndexPath: indexPath.row];
cell.label2.text=[someArray2 ObjectAtIndexPath: indexPath.row];
return cell;
}
this delegate method is gonna be called as many times as you returned value in numberOfRowsInSection DataSource method. Every time cell.label will be blank and you will add data to label by calling this line. so no need to create label each time as you did between line 79-90 here. http://pastebin.com/Vd4PKjTu
cell.label1.text=[someArray ObjectAtIndexPath: indexPath.row];
Creating custom cell means you create UI(i.e. .xib),interface & implementation (.h,.m file) for UITableViewCell by yourself and adopt them in your class's (i.e. DirectionsViewController.m) cellForRowAtIndexPath delegate method.
To load a custom cell, I've used this code (test it and it's working)
static NSString *CustomCellIdentifier = #"CustomCommentIdentifier";
detailedDirectionsViewCell *cell = (DetailedDirectionsViewCell *)[tableView dequeueReusableCellWithIdentifier:CustomCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DetailedDirectionsViewCell" owner:self options:nil];
for (id oneObject in nib)
{
if ([oneObject isKindOfClass:[DetailedDirectionsViewCell class]])
{
cell = (DetailedDirectionsViewCell *)oneObject;
}
}
}
and make sure that you changed the cell class nib file as the following:
Click on the files owner, change the type of it to NSObject (this for not to confuse when connect the Outlets.
Remove any view you have in the nib file, then, drag and drop a UITableViewCell component.
Change the super class of the component to the Cell class, in your case, change the cell from UITableViewCell to DetailedDirectionsViewCell.
Connect the outlet.
This should work, let me know if you have any question.
To answer your last question in Update 2, did you consider using a text field instead of a label? You should be able to disable editing so the user can't change what is displayed. If you need it to be a label, in the Attributes Inspector for the label, there is a property called "Lines" under the "Label" drop down that you can adjust. I'm not sure how to access that property programmaticly, but a quick Google search should help you out there.
In my case I had two different views for one cell class. And when I connected outlets to .h file they were connected only to the first view representation, not the second.
So to ensure that your outlets are really connected go Xcode me menu and open View -> Utilities -> Show FileConnection inspector, then make sure that your view's outlets are really connected.

issue when trying to create my own cell

I'm working on customizing my cell like below
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"reusedCell";
DetailCell *cell = (DetailCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Customize the cell of each row from table
if ( cell == nil ) {
NSLog(#" MY CELL IS NIL");
cell = [[DetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
else
NSLog(#" MY CELL IS NOT NIL");
}
By debugging, I know that line 4-5 are not reached at all
MY CELL IS NOT NIL
Therefore, I cant create my own cell. According to apple documentation
Typically, the first thing the data source should do is send dequeueReusableCellWithIdentifier: to the table view, passing in a reuse-identifier string. If the table view does not return a reusable cell object, the data source creates one, assigning the object a reuse identifier in the final parameter of initWithStyle:reuseIdentifier:
Therefore, in my situation, the table view does return a reusable cell object... Where it comes from then...
Have any one experienced this issue before. Please help thanks.
The first time a list is shown, cell will always be nil. Then as cells are scrolled off the ends, they are recycled through the reusable list. So sometimes when you debug, the cell is nil, and sometimes you get a recycled cell. Also, if you use the same identifier (i.e. #"reusedCell") on multiple lists (which you shouldn't), a cell from another list may get recycled into your list. That's why you should keep the cell identifiers unique.
If you want to create new cells each time when the user scrolls the table means, you need to override the reuseIdentifier property of the UITableViewCell in your custom cell class.
- (NSString *) reuseIdentifier
{
return #"";
}
This will let you to create new cell. But, check out the memory usage. Bcoz you are creating new cell each time when the user scrolls the table.

How to make a UITableViewCell with different subviews reusable?

I have a UITableView in which I display, naturally, UITableViewCells which are all of the same class, let's call it MyCell. So I have one MyCell.xib, one MyCell.h and one MyCell.m.
Unfortunately, this cells do contain one subview, which holds varying content, e.g. a train subview and a car subview. So if the UITableView is in need of a new cell, it's always a MyCell but sometimes it contains a train subview and sometimes a car subview.
Now, here is my problem: How to make MyCell properly reusable? The cell itself is reusable as intended (In the .xib I defined it's identifier) but it's subview has to be created again and again for every cell. My first idea was to change the identifier of MyCell depending on it's content but unfortunately, reuseIdentifier can't be changed on runtime.
I could, however, implement my own - (NSString *) reuseIdentifier {} which I guess would work, though I wouldn't consider it great style. Is there a better way to do this?
Many thanks in advance!
EDIT: I realize I need to add that the subviews are stored in their own classes/xibs to keep their code seperated.
Instead of adding subviews to cells I'd suggest that you create for every kind of cell your own class. If you have the kinds: train, car, bike, boat and airplane I would create five subclasses.
As I understand Apple the reuse mechanism with the identifier is just for that case: different types of cells get their own identifier, not every single cell a special one. Just to point how I interprete the whole thing.
In Apple's Table View Programming Guide for iOS / Characteristics of Cell Objects, the 3rd paragrpah delivers some insight into the meaning of the reuse identifier.
I've written myself a small TableViewCellFactory class which makes my life easier to create cells with the interface builder and have those in my app within minutes.
First of all a small example on how to use cellForRowAtIndexPath and the factory as well as setting content for a cell.
I create a fresh cell with the factory which needs the tableView so it can handle the reuse logic. Next thing is to let a method fill in the content for the cell. In this case it's a cell which shows a video clip with some text.
Data Source delegate method and helper
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)anIndexPath
{
VideoClipTableViewCell *cell = [TableViewCellFactory videoClipTableViewCellWithTableView:aTableView];
[self configureVideoClipCellWithCell:cell andIndexPath:anIndexPath];
// code to decide what kind of cell not shown, but it could be here, just move the model
// access code from the configure cell up here and decide on what you get
return cell;
}
Next comes the data source helper to put content into the cell. Get the content from my model array and set the properties. Note, this does everything by reference, nothing is returned.
- (void)configureVideoClipCellWithCell:(VideoClipTableViewCell *)aCell andIndexPath:(NSIndexPath *)anIndexPath
{
VideoClip *videoClip = [videoClips objectAtIndex:anIndexPath.row];
aCell.videoTitleLabel.text = videoClip.title;
aCell.dateLabel.text = videoClip.date;
// more data setting ...
}
TableViewFactory
This class consists mainly of convenience methods and some boilerplate methods to do the real work.
// Convenience static method to create a VideoClipTableViewCell
+ (VideoClipTableViewCell *)videoClipTableViewCellWithTableView:(UITableView *)aTableView
{
return [self loadCellWithName:#"VideoClipTableViewCell" tableView:aTableView];
}
// method to simplify cell loading
+ (id)loadCellWithName:(NSString *)aName tableView:(UITableView *)aTableView
{
return [self loadCellWithName:aName
className:aName
identifier:aName
tableView:aTableView];
}
// method with actually tries to create the cell
+ (id)loadCellWithName:(NSString *)aName
className:(NSString *)aClassName
identifier:(NSString *)anIdentifier
tableView:(UITableView *)aTableView
{
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:anIdentifier];
if (cell == nil) {
UINib * nib = [UINib nibWithNibName:aName bundle:nil];
NSArray * nibContent = nil;
nibContent = [nib instantiateWithOwner:nil options:nil];
for (id item in nibContent) {
if ([item isKindOfClass:NSClassFromString(aClassName)]) {
cell = item;
}
}
}
return cell;
}
I've thrown out the whole error and exception handling just to keep the example short. If someone is interested I'd add the code.
Some important things about the usage is:
The connected class name, the reuse
identifier and the nib name are all
the same so a cell can be created
with only one string constant, else
the long loadCellWithName has to be
used.
Don't forget to set the reuse identifier in interface builder.
The nib should contain only one TableViewCell (can be changed with some coding though)
Don't set outlets of the File's Owner, use those of the tableViewCell
Set the class identity of the cell to a corresponding class which should be created foremost
Look at the screenshot
Thoughts on subclassing own custom cells
It's actually easy to subclass your own cell, add a few properties to it, make them available in IB with outlets, choose the new extended class in IB for your nib file.
The main problem is interface itself. It's not easily done to have different kinds of cells based on a custom cell in interface builder. The first approach would be to copy the nib file, rename it and use it with all the existing references and link the new ones to differing outlets. But what happens if the base cell has to be changed? Going through all kinds of inheriting cells could be a tedious task.
I just stumbled across Custom views in Interface Builder using IBPlugins on Cocoa with Love. It's a nice tutorial how to extend the components Library in IB. Our custom base cell could become an item in the library and become the template we've been looking for. I think this approach is the right way to choose. Still, looking at necessary steps, it's not just done within 5 minutes.
Interface builder is a helpful tool, allowing us to rapidly create views, but when it comes to reusability through subclassing, there are big steps necessary to create maintainable views. A pity.
Creating the views with code only I think one is better off with subclassing if it comes to more than one level of inheritance or many ancestor classes for just one base view.
EDIT
On the other hand, Apple warns about excessive use of subviews in a cell:
However, if the content of a cell is
composed of more than three or four
subviews, scrolling performance might
suffer. In this case (and especially
if the cell is not editable), consider
drawing directly in one subview of the
cell’s content view. The gist of this
guideline is that, when implementing
custom table-view cells, be aware that
there is a tradeoff between optimal
scrolling performance and optimal
editing or reordering performance.
Right now any approach has its drawbacks and advantages:
Too man subviews will hit
performance, easily done with IB
Drawing with code will result in a
hard to maintain code base but will
perform better
Skipping IB makes
subclasssing of template cell classes
easier
Hierarchy through subclassing
difficult to achieve with IB with nib
files
There are a couple of different ways to do this. You need a way to access that subview and reset or change it on reuse.
You could subclass UITableViewCell with your own class of cell that has a property for the train or car view. That way you could access and change that view when the cell is reused.
Assign a different identifier to each type of cell:
`
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CarCellIdentifier = #"CarCell";
static NSString *TrainCellIdentifier = #"TrainCell";
if(indexPath == carCellNeeded) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CarCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CarCellIdentifier] autorelease];
[cell addSubview:carView];
}
} else if(indexPath == trainCellNeeded){
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TrainCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TrainCellIdentifier] autorelease];
[cell addSubview:trainView];
}
}
return cell;
}
Or assign a special tag to that sub view you are adding and when the cell comes back around again to be reused you can access that specific subview by its tag.
I would add both custom subviews to the nib and connect them to an outlet. And depending on the content I would hide one of them when you configure the content of your cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = /* load from nib */
}
if (/*indexPath conditionForTrainCell*/) {
cell.trainSubview.hidden = NO;
cell.carSubview.hidden = YES;
// configure train cell
}
else {
cell.trainSubview.hidden = YES;
cell.carSubview.hidden = NO;
// configure car cell
}
return cell;
}
the simplest is to make a custom UITableViewCell subclass and create a xib for it. Set the root view in the xib as an uitableviewCell and set its class to your UITableViewCell subclass. Set the file owner class as your TableViewController subclass and add all the subviews you want to it. Then you can simply:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = #"cellIdentifier";
TableViewMessageCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([TableViewMessageCell class])
owner:self
options:nil] lastObject];
}
Message * message = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.message = message;
return cell;
}

UITableView won't update properly (even with call to reloadData)

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.
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.
I had this same problem, and it was because I had a [tableView beginUpdates] call without an endUpdates call after.
Have you tried [tableView setNeedsDisplay:YES]?
After calling -reloadData, do you recieve callback to tableView:numberOfRowsInSection: ?
I'm almost sure, that self.myTableView is nil here:
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[self.myTableView reloadData];
}