I have a problem with Custom cell on story board. I need to access labels from the method called
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
How can i define it in my code? When I used IBOutlet Then It may caused the error.
So how could I access the label like
cell.textlable.text ??
Thanks alot.
I would subclass UITableViewCell. Inside the subclass create the IBOutlets and then you can access them in a regular way and set them up inside interface builder. Just be sure and set your prototype cell to be of that class. then the outlets will show up and you can access them with dot-syntax like you wanted.
An example would be:
#interface CustomCell : UITableViewCell
{
}
#property (nonatomic, retain) IBOutlet UILabel* customLabel;
#end
and the implementation is just as simple
#import CustomCell.h
#implementation CustomCell
#synthesize customLabel;
#end
It is that simple,
Then in your method you would do something like this:
CustonmCell* cell = [tableView dequeueReusableCellWithIdentifier:#"customCell"];
cell.customLabel = //whatever
//or
UILabel* mylabel = cell.customLabel;
You can add as many outlets as you would like and access them in a similar manner.
One common solution is to give each label a tag in the storyboard, and then find the label using viewWithTag: to find it in your code, like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = blah blah blah ...
...
UILabel *myLabel = [cell viewWithTag:1];
UILabel *anotherLabel = [cell viewWithTag:2];
// etc.
}
For custom cell in storyboard, use cellWillDisplay method instead of cellForRow to access cell variables.
Step 1. Create a custom MyTableViewCell:UITableViewCell class. Place the IBOutlet variables in this class.
Step 2. On Xcode IB, select the cell, change its class to MyTableViewCell. Then link the IBOutlets.
Step 3. In cellWillDisplayAtIndexPath, access the IBOutlet variables cell.myTextLabel as usual.
EDIT: Correction, if you're using Dynamic prototype, then cellForRowAtIndexPath will work. If you're using static cell then use cellWillDisplayAtIndexPath. If you're using static cell, then the steps above are not applicable, as you will be defining your IBOutlets at UITableView's view controller. Sorry for the confusion.
Just an edit to Robs answer. Took me a while as it came up with an error in xcode 4. Changed it to this:
UILabel *myLabel = (UILabel *)[cell.contentView viewWithTag:1];
Related
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.
I have a UITextField in My CustomCell of UITableview and i am using this UITableview as Subview of Mainview.There is another UITableview Which is also on my MainView but i am using this UITableview as Subclass of my MainView.Below image will also be hlepfull to Understand my problem.
As image show that above UITableview is my SubClass UITableviw and Below one Which is single row UITableview with CustomCell having UITextField. Now i want that when i Click on any Cell of SubClass UITableview my UITextField Text is Clear which i have in CustomCell of UITableview.
Note:- To Make my question more easir i have some code which works for me when i have UITextfield Simply on my MainView (not in CustomCell of UITableview).here is my Code.
In Mainview.h file
#import <UIKit/UIKit.h>
#import "Inputtableview.h"
#interface testingViewController : UIViewController<UITextFieldDelegate,UITableViewDelegate,UITableViewDataSource> {
IBOutlet UITextField *txtfinput;
Inputtableview *inputview;
IBOutlet UITableView *inputtbl;
}
#property (nonatomic, retain) UITextField *txtfinput;
#end
And then in ViewDidload of Mainview.m file
- (void)viewDidLoad {
if (inputview == nil) {
inputview = [[Inputtableview alloc] init];
}
[inputtbl setDataSource:inputview];
[inputtbl setDelegate:inputview];
inputview.view = inputview.tableView;
inputview.myAccessToMaintxtf = txtfinput;
[super viewDidLoad];
}
Now in my Subclass of UITableview.h file
#import <UIKit/UIKit.h>
#interface Inputtableview : UITableViewController<UITableViewDelegate,UITableViewDataSource> {
}
#property (nonatomic, assign) UITextField *myAccessToMaintxtf;
#end
And Finally in My SubClass UITableview.m file
#implementation Inputtableview
#synthesize myAccessToMaintxtf;
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
myAccessToMaintxtf.text=#"";
}
As i mention Earliar the Above Code works Fine when I have TextField in my MainView,But Did't know How to fix it When i Want to Clear the text of UITextField in CustomCell of UITableview.Any help Will be Appriated.Thanx in Advance.
Ok, I can't see how you are connecting "myAccessToMaintxtf"to the textfield in your CustomCell. What I use for getting subviews from my custom cells are "tags". A tag can be used like an identifier for your subviews. So, if you are using a xib you can set the tag in the settings tab of your textfield, and if you are creating the textfield by code you can set the tag like:
myAccessToMaintxtf.tag = 11; //11 is and example number, use the number that you want
Then when you want to access to your textfield use:
theTextFieldToClear = (UITextField*)[theCellWithTheTextFieldToClear viewWithTag:11];
You can find more information about tags in the UIView Class Reference:
http://developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/uiview/uiview.html
Good Luck
EDIT 2:
If you are using two tables, and you want to clear a textfield in one table, selecting the other one, i think that you need something to make the link between the indexPath selected in one table and the indexPath for the cell with the textfield. If you are using something like:
table 1, indexpath:0,0 for celcius;
table 1, indexpath:0,1 for farenheit;
table 1, indexpath:0,2 for kelvin;
and
table 2, indexpath:0,0 for celcius;
table 2, indexpath:0,1 for farenheit;
table 2, indexpath:0,2 for kelvin;
You just can use the same indexPath to get the cell with the textfield FROM THE OTHER TABLEVIEW (Inputtableview) like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cellWithTheFieldToClear = [yourInputTableView cellForRowAtIndexPath:indexPath];
theTextFieldToClear = (UITextField*)[theCellWithTheTextFieldToClear viewWithTag:2];
theTextFieldToClear.text = #"";
}
Did you add your myAccessToMaintxtf as subview of the UITableViewCell in dataSource cellForRowAtIndexPath? If you did, you still have to reload the cell using [tableView reloadCellAtIndexPath:...];
Or another suggestion is to subclass the UITableViewCell, and add an Outlet property, connect the outlet to the TextField. Then you can do something like selectedCell.textField.text = #""; in you didSelectRowAtIndexPath.
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];
How do I make a reusable TableViewCell in Interface Builder with changeable labels?
Is this even possible? From what I understand apple has been giving custom TableViewCell in Interface Builder some love lately, so this should be possible?
Ps. I know there are a lot of questions with answers about TableViewCell in IB, but I couldn't find anyone that made labels work.
You can change anything in a cell that is being re-used. To customize labels that you create in IB, you should set their tags in IB itself & fetch the label using the same tag.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MyCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[MyCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
//Do anything here with the labels. Even add or remove them.
(UILabel*) label1 = (UILabel*)[cell viewWithTag:1];
return cell;
}
I used to do it he same way as in accepted answer, but I've always felt using tags like I'm using "go to" in Pascal. Feels dirty. But maybe it's just me, tags work just fine.
There's an alternative way though. Subclass a UITableViewCell, create an IBOutlet property, connect in IB, and reference your property in cellForRowAtIndexPath: code. Like so:
interface MyCustomCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *myAwesomeLabel;
#end
Don't forget to set you cell's class to MyCustomCell in IB.
After that you can connect your property in IB directly like so
And in your Table View Data Source now you can access this property
#import "MyCustomCell.h"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell) {
cell.myAwesomeLabel.text = #"Hello, World!";
}
return cell;
}
Using tags is error-prone and might turn into a mess very quickly if you use a lot of them.
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.