UITableView issue when using a separate delegate / data source

General description:

To get started with what works, I have a UITableView that has been placed in an Xcode-generated view using Interface Builder. The owner of the view file is configured to a subclass created by Xcode, UIViewController . In this subclass, I added the working implementations of numberOfSectionsInTableView: tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath: and the Table View dataSource and delegate connected to this class through the File Owner in Interface Builder.

The above configuration works without problems. The problem arises when I want to move this kind of dataSource table and delegate changes to a separate class, most likely because there are other controls in the view besides the table view, and I would like to move the table view related code with its own class. To do this, try the following:

  • Create a new subclass of UITableViewController in Xcode
  • Move the well-known good implementations of numberOfSectionsInTableView: tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath: to a new subclass
  • Drag the UITableViewController to the top level of the existing XIB in the InterfaceBuilder, remove the UIView / UITableView that are automatically created for that UITableViewController , then set the UITableViewController class to the new subclass
  • Remove previously working UITableView existing dataSource and delegate connections and connect them to the new UITableViewController

When done, I don't have a working UITableView . I get one of three results that may seem random:

  • When a UITableView loads, I get a runtime error indicating that I am sending tableView:cellForRowAtIndexPath: object that does not recognize it.
  • When a UITableView loads, the project breaks into a debugger without errors
  • No error, but UITableView not showing

With some debugging and creating a basic project to reproduce this problem, I usually see the third option above (no error, but no visible table view). I added some calls to NSLog and found that although numberOfSectionsInTableView: and numberOfRowsInSection: both called, cellForRowAtIndexPath: no. I am convinced that I missed something very simple and hoped that the answer might be obvious to someone who has more experience than me. If this is not an easy answer, I would be happy to update the code or sample project. Thank you for your time!

Complete steps to play:

  • Create a new iPhone OS, View-based app in Xcode and name it TableTest
  • Open TableTestViewController.xib in Interface Builder and drag the UITableView onto the provided surface.
  • Connect the UITableView dataSource and delegate -outlets to the file owner, which should already represent the TableTestViewController class. Save the changes.
  • Return to Xcode, add the following code to TableTestViewController.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 in a UITableView

  • Now, to try to move this UITableView logic into a separate class, first create a new file in Xcode by selecting a subclass of UITableViewController and calling the TableTestTableViewController class

  • Remove the above code snippet from TableTestViewController.m and place it in TableTestTableViewController.m , replacing the standard implementation of these three methods with ours.
  • In the Builder interface, in the same TableTestViewController.xib file TableTestViewController.xib drag the UITableViewController into the main IB window and delete the new UITableView object that automatically appeared with it
  • Set the class for this new UITableViewController to TableTestTableViewController
  • Remove the dataSource and delegate bindings from the previously existing UITableView and reconnect the same two bindings to the new TableTestTableViewController that we created.
  • Keep changes, build and go, and if you get the results I get, please note that the UITableView no longer functioning properly

Solution: A few more troubleshooting problems and some help from the iPhone Developer Forums , I have documented a solution! The main subclass of the project's UIViewController requires an output that points to an instance of the UITableViewController . To do this, simply add the following to the header of the main view ( TableTestViewController.h ):

 #import "TableTestTableViewController.h" 

and

 IBOutlet TableTestTableViewController *myTableViewController; 

Then, in the Builder interface, connect the new outlet from File Owner to the TableTestTableViewController in the IB main window. No changes are required in the XIB user interface part. Just having this outlet in place, even if the user code does not use it directly, it completely fixes the problem. Thanks to those who helped and credited BaldEagle on the iPhone developer forums to find a solution.

+54
objective-c iphone cocoa-touch uitableview
Oct. 31 '08 at 17:48
source share
4 answers

I followed your steps, recreated the project and ran into the same problem. Mostly you're almost there. There are 2 things left (after fixing them):

  • You need to connect tableView to TableTestTableViewController to the UITableView that you have on the screen. As I said, because this is not an IBOutlet , you can override the tableView property and make it and IBOutlet :

     @interface TableTestTableViewController : UITableViewController { UITableView *tableView; } @property (nonatomic, retain) IBOutlet UITableView *tableView; 
  • The next is to add a link to the TableTestTableViewController and save it to the TableTestViewController . Otherwise, your TableTestTableViewController may be released (after loading the thread with nothing to hang on it). This is why you see erratic results, failures, or nothing. To do this, add:

     @interface TableTestViewController : UIViewController { TableTestTableViewController *tableViewController; } @property (nonatomic, retain) IBOutlet TableTestTableViewController *tableViewController; 

    and connect this in the interface builder to the TableTestTableViewController instance.

With the above, it worked perfectly on my machine.

Also I think it would be useful to indicate the motivation for all this (instead of just using the UITableViewController with your own UITableView ). In my case, it was using other views that only had a UITableView on one content screen. Therefore, I can add other UILabels or UIImages under the UIView and show the UITableView below or above them.

+23
Nov 01 '08 at 18:03
source share

I just spent many hours pulling out my hair, trying to understand why the UITableView did not appear when I put it in a separate tip, and not in the main tip. I finally found your discussion above and realized that this was because my UITableViewController not saved! Apparently, the delegate and datasource properties of the UITableView not marked as β€œsave”, and so my nib loads, but the controller was taken ... And due to the miracle of objective-c, I did not get any error messages ... I still do not understand why this is not a failure. I know that I saw a message sent to the released xxx before ... why didn't he give me one of these?!?

I think most developers have suggested that the structure they create in the interface builder will be stored in some larger context (Nib) and should not be freed. I guess I know why they do it. So the iPhone can fall and reload parts of the nib on low memory. But it’s hard for a person to understand.

Can someone tell me where I should have read about this behavior in the docs?

Also about connecting views. First, if you drag it from the user interface constructor, you will see that they connect the view property (which is an IBOutlet ) to the table view. No need to expose a tableView that seems to be set inside. In fact, you don’t even need to set the view if you do not want viewDidLoad notification. I just broke the view connection between the UITableView and the UITableViewController (only delegation and data set) and it seems to work fine.

+5
Mar 07 '09 at 4:43
source share

Yes, for some reason (please call if anyone knows why ...) tableView The UITableViewController property does not display as an IBOutlet , although it is publicly available. Therefore, when you use Interface Builder, you cannot see this property to connect to your other UITableView . Therefore, in your subclass, you can create the tableView property, marked as IBOutlet , and attach it.

It all seems like a hacky and workaround for me, but this is the only way to split UITableViewController's UITableView and put it somewhere else in the user interface hierarchy. I ran into the same problem when I was trying to create a view where there are things other than a UITableView , and that is how I solved it ... Does this fit right?

+3
Oct 31 '08 at 20:16
source share

I was able to do this job. I built a really beautiful toolbar with 4 TableViews and a webview with video. The key consists of separate tableView controllers and IBOutlets for other tableview controllers defined in the view controller. In the UIB, you just need to connect other tableview controllers to the owner of the view controller file. Then connect the tables to the appropriate view controllers for the data source and delegate.

+2
Mar 10 2018-12-12T00:
source share



All Articles