General Description:
To start with what works, I have a UITableView
which has been placed onto an Xcode-generated view using Interface Builder. The view's File Owner is set to an Xcode-generated subclass of UIViewController
. To this subclass I have added working implementations of numberOfSectionsInTableView: tableView:numberOfRowsInSection:
and tableView:cellForRowAtIndexPath:
and the Table View's dataSource
and delegate
are connected to this class via the File Owner in Interface Builder.
The above configuration works with no problems. The issue occurs when I want to move this Table View's dataSource
and delegate
-implementations out to a separate class, most likely because there are other controls on the View besides the Table View and I'd like to move the Table View-related code out to its own class. To accomplish this, I try the following:
UITableViewController
in XcodenumberOfSectionsInTableView:
, tableView:numberOfRowsInSection:
and tableView:cellForRowAtIndexPath:
to the new subclassUITableViewController
to the top level of the existing XIB in InterfaceBuilder, delete the UIView
/UITableView
that are automatically created for this UITableViewController
, then set the UITableViewController
's class to match the new subclassUITableView
's existing dataSource
and delegate
connections and connect them to the new UITableViewController
When complete, I do not have a working UITableView
. I end up with one of three outcomes which can seemingly happen at random:
UITableView
loads, I get a runtime error indicating I am sending tableView:cellForRowAtIndexPath:
to an object which does not recognize itUITableView
loads, the project breaks into the debugger without errorUITableView
does not appearWith some debugging and having created a basic project just to reproduce this issue, I am usually seeing the 3rd option above (no error but no visible table view). I added some NSLog calls and found that although numberOfSectionsInTableView:
and numberOfRowsInSection:
are both getting called, cellForRowAtIndexPath:
is not. I am convinced I'm missing something really simple and was hoping the answer may be obvious to someone with more experience than I have. If this doesn't turn out to be an easy answer I would be happy to update with some code or a sample project. Thanks for your time!
Complete steps to reproduce:
TableTest
TableTestViewController.xib
in Interface Builder and drag a UITableView
onto the provided view surface.UITableView
's dataSource
and delegate
-outlets to File's Owner, which should already represent the TableTestViewController
-class. Save your changesTableTestViewController.m:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { NSLog(@"Returning num sections"); return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSLog(@"Returning num rows"); return 1; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSLog(@"Trying to return cell"); static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease]; } cell.text = @"Hello"; NSLog(@"Returning cell"); return cell; }
Build and Go, and you should see the word Hello
appear in the UITableView
Now to attempt to move this UITableView
's logic out to a separate class, first create a new file in Xcode, choosing UITableViewController
subclass and calling the class TableTestTableViewController
TableTestViewController.m
and place it into TableTestTableViewController.m
, replacing the default implementation of these three methods with ours.TableTestViewController.xib
-file, drag a UITableViewController
into the main IB window and delete the new UITableView
object that automatically came with itUITableViewController
to TableTestTableViewController
dataSource
and delegate
bindings from the existing, previously-working UITableView
and reconnect the same two bindings to the new TableTestTableViewController
we created.UITableView
no longer functions properlySolution: With some more troubleshooting and some assistance from the iPhone Developer Forums, I've documented a solution! The main UIViewController
subclass of the project needs an outlet pointing to the UITableViewController
instance. To accomplish this, simply add the following to the primary view's header (TableTestViewController.h
):
#import "TableTestTableViewController.h"
and
IBOutlet TableTestTableViewController *myTableViewController;
Then, in Interface Builder, connect the new outlet from File's Owner to TableTestTableViewController
in the main IB window. No changes are necessary in the UI part of the XIB. Simply having this outlet in place, even though no user code directly uses it, resolves the problem completely. Thanks to those who've helped and credit goes to BaldEagle on the iPhone Developer Forums for finding the solution.
UITableview's Delegate Methods :The delegate of an UITableView object must adopt the UITableViewDelegate protocol. Optional methods of the protocol allow the delegate to manage selections, configure section headings and footers, help to delete and reorder cells, and perform other actions.
A data source is almost identical to a delegate. The difference is in the relationship with the delegating object. Instead of being delegated control of the user interface, a data source is delegated control of data.
Methods for managing selections, configuring section headers and footers, deleting and reordering cells, and performing other actions in a table view.
There are two main base ways to populate a tableview. The more popular is through Interface Building, using a prototype cell UI object. The other is strictly through code when you don't need a prototype cell from Interface Builder.
I followed your steps, recreated the project and ran into the same problem. Basically you are almost there. There are 2 things missing (once fixed it works):
You need to connect the tableView
of the TableTestTableViewController
to the UITableView
you have on the screen. As I said before because it is not IBOutlet
you can override the tableView
property and make it and IBOutlet
:
@interface TableTestTableViewController : UITableViewController { UITableView *tableView; } @property (nonatomic, retain) IBOutlet UITableView *tableView;
Next thing is to add a reference to the TableTestTableViewController
and retain it in the TableTestViewController
. Otherwise your TableTestTableViewController
may be released (after loading the nib with nothing hanging on to it.) and that is why you are seeing the erratic results, crashes or nothing showing. To do that add:
@interface TableTestViewController : UIViewController { TableTestTableViewController *tableViewController; } @property (nonatomic, retain) IBOutlet TableTestTableViewController *tableViewController;
and connect that in the Interface Builder to the TableTestTableViewController
instance.
With the above this worked fine on my machine.
Also I think it would be good to state the motivation behind all this (instead of just using the UITableViewController
with its own UITableView
). In my case it was to use other views that just the UITableView
on the same screenful of content. So I can add other UILabels
or UIImages
under UIView
and show the UITableView
under them or above them.
I just spent many hours pulling my hair out trying to figure out why a UITableView
wouldn't show up when when I had it embedded in a separate nib instead of in the main nib. I finally found your discussion above and realized that it was because my UITableViewController
wasn't being retained! Apparently the delegate and datasource properties of UITableView
are not marked "retain" and so my nib was loading but the controller was getting tossed... And due to the wonders of objective-c I got no error messages at all from this... I still don't understand why it didn't crash. I know that I've seen "message sent to released xxx" before... why wasn't it giving me one of those?!?
I think most developers would assume that structure that they build in an interface builder would be held in some larger context (the Nib) and not subject to release. I guess I know why they do this.. so that the iPhone can drop and reload parts of the nib on low memory. But man, that was hard to figure out.
Can someone tell me where I should have read about that behavior in the docs?
Also - about hooking up the view. First, if you drag one in from the UI builder you'll see that they hook up the view property (which is an IBOutlet
) to the table view. It's not necessary to expose the tableView
, that seems to get set internally. In fact it doesn't even seem to be necessary to set the view unless you want viewDidLoad
notification. I've just broken the view connection between my uitableview
and uitableviewcontroller
(only delegate and datasource set) and it's apparently working fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With