I have a UITableView and it contains a custom UITableViewCell. To test, I have an array that has three strings in it. The UITableView delegate methods are called as expected, however, the tableView:cellForRowAtIndexPath delegate is always passed an NSIndexPath instance whose row property is always == nil:
tableView:cellForRowAtIndexPath is called 3 times (once for each object in my array). I added the tableView from within the designer (storyboard) and created an outlet for it. The UITableViewCell instances appear to be correctly instantiated which each call to this delegate. I just can't wrap my head around why the [indexPath row] value is always nil.
Interface(s):
In the implmentation file:
#interface FirstViewController ()
#property(nonatomic, strong)AppDelegate *sharedDelegate;
#property(nonatomic, strong)NSArray *userList;
#end
In the header:
#interface FirstViewController : UITableViewController <FacebookDelegate>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
Init the custom cell:
-(void)viewDidLoad
{
[self.tableView registerClass: [ListCategoryCell class]forCellReuseIdentifier:#"ListCategoryCell"];
self.userList = #[#"d", #"g", #"f"]; // make some test data
}
And the delegate this is driving me mad:
//NSIndexPath.row is nil ?!
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"ListCategoryCell";
ListCategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = (ListCategoryCell *)[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
cell.titleLabel.text = [self.userList objectAtIndex:[indexPath row]];
cell.detailLabel.text = #"Detail";
return cell;
}
Am I missing something? Thanks!
Working Now
I left out some context (and I should not have) that I believe was very relevant to my problem. I created a UIViewController originally and then added a UITableView to this as the view. In the UITableView I created a custom prototype cell. I did all the house work:
UIViewController implemented the UITableViewDelegate & UITableViewDatasource.
Created and outlet for the UITableView.
Hooked up all the outlets
Everything seemed to work except for the fact that indextPath.row property was always nil. Some resources I found suggested that custom cells were not visible before the uitableview delegates were called.
In the end I made my class a subclass of UITableViewController. Things started working. I am still curious why my original attempt was failing.
Thanks for everyone's time. Some great comments helped me investigate some topics that are "good to know".
You need to provide at least two methods in view controller if you want it to manage your table view. They are:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
You've already provided the second, so your table view actually can produce cells but it doesn't know how many.The default value the first method returns is nil.That is the reason why you don't even have an index path.
Optionally:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
The default value is one,so you don't need to override this in case you have only one section
Make sure your view controller also follows delegate and datasource protocols.
Related
In my app I wish to have a UIViewController that will hold a UITableView in it. In this UITableView I wish to have a customized UITableViewCell (i.e. I wish to define my own elements in this CELL - image, labels and buttons - as seen in the image below). And... I want to create them in the Storyboard.
Now, setting the elements in the Storyboard is easy.
I understand how to connect the UITableView and set it in the UIViewController (including the delegates in the .h file and using the basic table delegate methods).
What I'm not clear about, is how to connect and control the customized UITableViewCell and its outlets. Can I create the Outlets and Actions within the UIViewController .h and .m files? Do I need to create a separated UITableViewCell.h/.m files and call on them in the cellForRowAtIndexPath method?
Can anyone suggest what's the best approach for my needs?
UPDATE:
Here is the code I used in cellForRowAtIndexPath while using the separated MyCell.h/m file option. This code is written in the ViewController.m file, where the UITableView is implemented.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ContentCell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//MyCell is the Objective-C Class I created to manage the table cells attributes.
//#"ContentCell" is what I had entered in the Storyboard>>UITableViewCell as the Cell Identifier.
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
//Here is the place I'm not clear about - Am I supposed to init the cell in a different way? If so, how?
}
cell.contentNameLabel.text = [self.dataArray objectAtIndex: [indexPath row]];
// this is the test I had done to check if the new "MyCell" object is actually getting what I would expect it to get. well... the text gets updated in the relevant label, so i guess it gets it
return cell;
}
When running the app, using the debugger break point, I can see that the code always skips the "if (cell == nil)" and never enters the code where the new MyCell object supposes to be allocated and initiated. Any idea what may I be doing wrong?
Correct, create separate UITableViewCell.h/.m files to match your custom UITableViewCell class and call on them in your cellForRowAtIndexPath method.
In your storyboard, set the class of your custom UITableViewCell to your custom class (e.g. CustomTableCell).
Your custom UITableViewCell class would contain IBOutlets which you would wire up in your storyboard, here is an example:
CustomTableCell.h:
#import "CustomStuff.h" // A custom data class, for this example
#interface CustomTableCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *titleLabel;
- (void)configureForCustomStuff:(CustomStuff *)stuff;
#end
CustomTableCell.m:
#import "CustomTableCell.h"
#implementation CustomTableCell
#synthesize titleLabel;
#pragma mark - Configure the table view cell
- (void)configureForCustomStuff:(CustomStuff *)stuff
{
// Set your outlets here, e.g.
self.titleLabel.text = stuff.title;
}
#end
Then, in your cellForRowAtIndexPath method, configure your cell:
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:#"CustomCellID"];
// Your custom data here
CustomStuff *customStuff = <YOUR CUSTOM STUFF>
[cell configureForCustomStuff:customStuff];
So I have looked around a quite a bit, and nothing on here seems to explain exactly the correct way of doing this. I have 7 UITextFields within a custom UITableViewCell.
My question is this: What is the correct way of managing the delegate of these UITextFields?
Since the custom cells are technically part of the "model" portion of the project, I would rather have the controller that controls the UITableView also control the text fields in the table's cells, but I can not figure out how to set the delegate for the text fields (which are created in the subclass of UITableViewCell) to this view controller.
Would it be bad practice to just make the subclass of UITableViewCell conform to UITextField delegate and manage all of that stuff in there? If so, how else should I go about doing this?
Thanks, any help would be appreciated.
You shouldn't have a problem setting the delegate of the cell's text field to be your view controller.
This is what you need to do:
1) The view controller needs to implement the UITextFieldDelegate protocol
2) Declare a property for the text field in your custom cell
#property (nonatomic, retain) IBOutlet UITextField *textField;
3) Then set the view controller as the text field's delegate in the method cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
// use this if you created your cell with IB
cell = [[[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil] objectAtIndex:0];
// otherwise use this
cell = [[[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// now set the view controller as the text field delegate
cell.textField.delegate = self;
}
// configure cell...
return cell;
}
In my opinion the cell should manage the keyboard since it is the one that is holding the UITextField. You can set your cell as the UITextField delegate. In my own application, I have done this and then made my cell have it's own delegate. Any of the methods of UITextField or any new methods that should be handled by the controller can be passed along to the controller through the cells delegate.
In this way the cell can still be generic without knowing anything about what the application is actually doing.
My suggestion would be to "tag" (i.e. set the tag) of each textField with a value that encodes the section, row, and one-of-7 text views in the table, then make the UIViewController the delegate.
So you need to bound the size of these - say you will never have more than 100 rows. So you encode this as:
.tag = 1000*section + 100*row +
When you get a message you can have a method/function take the tag and decode it into section, row, tag, and do what you need to have done.
To declare your TableViewController as the delegate include <UITextFieldDelegate> at the end of your #interface in the TableViewController's .h file.
#interface MyTableViewController : UITableViewController <UITextFieldDelegate>
Then, connect the text fields by ctrl-dragging each field under the #interface. Each UITextField is connected to its respective property by an IBOutlet. Finally, in the .m file include the following function to show the delegate which field you want to return....
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[aTextField resignFirstResponder];
return YES;
}
Swift version (Based on Eyal's answer)
class MyViewController: UIViewController, ... , UITextFieldDelegate {
#IBOutlet var activeTextField: UITextField! //doesn't need to connect to the outlet of textfield in storyboard
....
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
....
var cellTextField = self.view.viewWithTag(101) as? UITextField
cellTextField!.delegate = self;
....
}
I'm a little confused with something. I'm trying to create a custom cell and I want to use the interface builder way.
The normal way I create a table is to have the table as:
.h
#interface AssessList : UIViewController {
IBOutlet UITableView *tblAssessList;
}
#property(nonatomic, retain) UITableView *tblAssessList;
#end
.m
- (NSInteger)
numberOfSectionsInTableView:(UITableView *)tableView {
return groupArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return totalArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = #"I am the text....";
return cell;
}
Now i've created a new class for the cell and I think I understand how to put that in. But can I leave the .h as
#interface AssessList : UIViewController
or does the class/nib with the full table on it have to be a UITableViewController?
Tom
does the class/nib with the full table on it have to be a
UITableViewController?
No. A UITableViewController is just a convenience UIViewController subclass which has a UITableView and is already setup as its delegate/datasource (it is declared as conforming to the UITableViewDelegate and UITableViewDatasource protocols), it also has pre-filled method implementations for these protocols in the template implementation file which Xcode generates for you. You can just as well do all of this yourself, I often do.
You should however make an IBOutlet for your UITableViewCell so that you can load it from the nib file (see the Loading Custom Table-View Cells From Nib Files in the Table View Programming Guide).
If you want to do in the Interface Builder Way, then create an xib (view xib). Drag and drop a UITableViewCell object from the obj palette. Customize it as you wish. In the tableView:cellForRowAtIndexPath: method, do this:
UITableViewCell * aCell = [tableview dequeueReusableCellWithIdentifier:#"SomeIdentifier"];
if (aCell == nil)
{
NSArray *arr = [[NSBundle mainBundle] loadNibNamed:#"CustomCellNibName" owner:self options:nil];
for (NSObject *anObj in arr) {
if([anObj isKindOfClass:[UITableViewCell class]]) {
aCell = (UITableViewCell *)anObj;
}
}
}
The identifier for the tableviewcell can be set in the IB.
I guess it should be sub class of UItableViewCell
i.e.
#interface AssessList : UITableViewCell
When you want a custom tableview cell you will also need a subclass of UITableViewCell..
A tutorial can be found on this blog
Keep in mind that quite a few things can be done without creating a custom cell, this includes adding the switch to make your tableview look like the one from settings.app, to the way the iPod displays songs.
In the assessList Class you are using the custom cell created in otherviewController (UITableViewCell subclass) so there is no need to change this line
#interface AssessList : UIViewController
Note:- the otherviewController should be a subclass of UITableViewCell
I am just curious. In IB, we can put a tableviewcontroller. However, as far as I know, we always subclass that tableview controller right? That way we can implement delegate, etc.
However, it seems that for some "default" behavior, IPhone intended tableviewcontroller to be used as is. Otherwise, why would IB let us put tableViewController like that?
Are there any sample code where people use tableViewController without subclassing?
Where does they implement things like what cells to draw, etc. then?
I guess the right answer of the question is that it's simply ridiculous to use a UITableViewController without sub classing. No body is doing it. Please correct me if I am wrong. I am just curious.
Whether you use a subclass of UITableViewController or UIViewController you need to set the data your table is going to display, otherwise, what's the point of a blank table? To achieve that you have to subclass and implement some methods. It's also a good idea to keep the delegate and the datasource in the same controller, unless the complexity really asks for different classes.
That being said, I always create my own table controllers as a subclass of UIViewController and implement the table controllers methods myself, because it gives you more flexibility. Matt Gallagher has several posts on how and why. See UITableView construction, drawing and management (revisited).
If you want to give it a try, create a subclass of UIViewController with a XIB and add the following sample code:
// interface
#import <UIKit/UIKit.h>
#interface SettingsVC : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) NSMutableArray *array;
#end
// implementation
#synthesize tableView = _tableView;
#synthesize array = _array;
# pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.array count];
}
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
int row = [indexPath row];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [self.array objectAtIndex:row];
return cell;
}
Then add a UITableView object to the XIB, link the tableView of the controller to the UITableView object, and link the delegate and datasource of the UITableView to the controller.
No, this is not necessary to inherit your class with tableViewController. You can use table view by simply
putting TableViewController in xib.
and setting its delegate and datasourse to file's owner you can draw the table cells.
I don't think you can use a UITableViewController as is, it's like using a UIViewController without subclassing it : you can't set any inner mechanics.
But you can have a UITableView without using a UITableViewController.
Sure you can use UITableViewController without subclassing it.
Samplecode is very easy and straight forward.
For example like this:
- (IBAction)selectSomeOption:(id)sender {
UITableViewController *tableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStyleGrouped];
tableViewController.tableView.dataSource = self;
tableViewController.tableView.delegate = self;
tableViewController.title = "Select some option";
[self.navigationController pushViewController:tableViewController animated:YES];
}
and the UITableViewDatasource and Delegate methods go into the same class.
Sure, if you like pain you could create a UIViewController in code and add a tableView on your own.
Or create a subclass for such an easy task.
The use of a non subclassed UITableViewController is sometimes convenient.
How can I access the UIView in which I have a UITableView, from the custom cells inside that table. I cant find a method to do that.
Thanks
You can add an instance variable that points to the UITableView and set it when creating/configuring the cell (e.g. in tableView:cellForRowAtIndexPath:). Make sure that your cell does not retain the tableView though.
Knowing your cell’s tableView, call [parentTableView superView] to access the UITableView’s parent view:
#interface PropertyListingCell : UITableViewCell {
__weak id parentTableView;
}
- (void) setParentTableView:(UITableView*)tv; // parentTableView = tv;
In UITableViewController implementation:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//dequeue/create and configure your custom cell here
[cell setParentTableView:tableView];
return cell;
}
UPDATE:
If you're using recent XCode (at least 4.3) you can simply add
#property (weak) UITableView *parentTableView; // use unsafe_unretained instead of weak if you're targeting iOS 4.x
to the #interface section of your UITableViewCell's subclass. Then, when you're creating a cell (in tableView:cellForRowAtIndexPath:) set this property accordingly:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ...
//dequeue/create and configure your custom cell here
// ...
cell.parentTableView = tableView;
return cell;
}
And in your cell class call self.parentTableView to access the tableView this cell belongs to.