I need to create my own UITableViewCell using the xib file, to draw the graphic interface...
what are the right steps to create my new class and to use into my UITableView?
thanks in advance
In iOS5 you'll want to use the new:
registerNib:forCellReuseIdentifier:
Which basically does the same thing...
Personally I think that both suggested tutorials have a big flaw when it comes to reuseIdentifier. If you forget to assign it in interface builder or misspell it, you will load the nib each and every time cellForRowAtIndexPath gets called.
Jeff LaMarche writes about this and how to fix it in this blog post. Apart from reuseIdentifier he uses the same approach as in the apple documentation on Loading Custom Table-View Cells From Nib Files.
After having read all these articles I came up with following code:
Edit: If you are targeting iOS 5.0 and higher you'll want to stick with Duane Fields' answer
#interface CustomCellWithXib : UITableViewCell
+ (NSString *)reuseIdentifier;
- (id)initWithOwner:(id)owner;
#end
#implementation CustomCellWithXib
+ (UINib*)nib
{
// singleton implementation to get a UINib object
static dispatch_once_t pred = 0;
__strong static UINib* _sharedNibObject = nil;
dispatch_once(&pred, ^{
_sharedNibObject = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil];
});
return _sharedNibObject;
}
- (NSString *)reuseIdentifier
{
return [[self class] reuseIdentifier];
}
+ (NSString *)reuseIdentifier
{
// return any identifier you like, in this case the class name
return NSStringFromClass([self class]);
}
- (id)initWithOwner:(id)owner
{
return [[[[self class] nib] instantiateWithOwner:owner options:nil] objectAtIndex:0];
}
#end
UINib (available in iOS 4.0 and later) is used here as a singleton, because although the reuseIdentifier is used, the cell still gets re-initialized about 10 times or so. Now cellForRowAtIndexPath looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCellWithXib *cell = [tableView dequeueReusableCellWithIdentifier:[CustomCellWithXib reuseIdentifier]];
if (cell == nil) {
cell = [[CustomCellWithXib alloc] initWithOwner:self];
}
// do additional cell configuration
return cell;
}
A video tutorial showing how to do this with Xcode 4.2 has been made. The author has written a blog post as well.
This tutorial takes you through the whole modern iOS 5 solution, from the process of creating the cell's xib and class files all the way to the finish:
http://mrmaksimize.com/ios/Custom-UITableViewCell-With-NIB/
`You can create custom cells in table view with the help of .xib file. First setup the table view in your view controller, create a new xib file with its class and use it in table view.
- (IBAction)moveToSubCategory:(id)sender;
#property (strong, nonatomic) IBOutlet UILabel *foodCategoryLabel;
#property (strong, nonatomic) IBOutlet UIImageView *cellBg;
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [foodCatArray count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ExampleCell";
ExampleCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ExampleCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
[cell setTag:indexPath.row];
cell.cellBg.image=[UIImage imageNamed:[photoArray objectAtIndex:indexPath.row]];
cell.foodCategoryLabel.text=[foodCatArray objectAtIndex:indexPath.row];
return cell;
}
You can create CustomCell class with XIB that is inherited from UITableViewCell. We will just add category in tableview class .m file in following way. I think this is the easiest method which an be applied for custom cell creation.
#interface UITableViewCell(NIB)
#property(nonatomic,readwrite,copy) NSString *reuseIdentifier;
#end
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 30;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier=#"cell";
CustomCell *cell=[tableView dequeueReusableCellWithIdentifier:identifier];
if(cell==nil)
{
NSLog(#"New Cell");
NSArray *nib=[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell=[nib objectAtIndex:0];
cell.reuseIdentifier=identifier;
}else{
NSLog(#"Reuse Cell");
}
cell.lbltitle.text=[NSString stringWithFormat:#"Level %d",indexPath.row];
id num=[_arrslidervalues objectAtIndex:indexPath.row];
cell.slider.value=[num floatValue];
return cell;
}
#end
Related
I am trying to create a custom table view cell with a xib but when I call:
CustomReferenceTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
I get the following:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSObject 0x7aa59c00> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key
Here is my setup:
- (void)viewWillAppear:(BOOL)animated
{
[self.tableView registerNib:[UINib nibWithNibName:#"CustomReferenceTableViewCell"
bundle:[NSBundle mainBundle]]
forCellReuseIdentifier:#"referenceCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"referenceCell";
CustomReferenceTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[CustomReferenceTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.number.text = #"1";
cell.referenceName.text = #"Name";
cell.reference.text = #"The text of the reference";
return cell;
}
Here is a picture of my connections in interface builder. Also my cellIdentifiers match between class and xib:
I have seen this post here but I still can't seem to get it work.
Help would be greatly appreciated.
Edit
Here is a pic showing setting of custom class. Below that is my initialiser for the tableViewCell:
Here is the header:
#import <UIKit/UIKit.h>
#interface CustomReferenceTableViewCell : UITableViewCell
#property (nonatomic, weak) IBOutlet UILabel *number;
#property (nonatomic, weak) IBOutlet UILabel *referenceName;
#property (nonatomic, weak) IBOutlet UILabel *reference;
#end
And here is the implementation:
#import "CustomReferenceTableViewCell.h"
#implementation CustomReferenceTableViewCell
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
I think you are setting the file owner of the cell to your custom cell which you shouldn't do. The way to get things working for a custom cell is,
In your xib, have a Table View Cell component an populate the contents of the cell in its ContentView.
Make your class CustomReferenceTableViewCell as the Custom Class of that cell component.
Now, do NOT register the cell in your tableview; instead do the following in the cellForRowAtIndexPath when your cell is nil after [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; and then register it:
cell = [self.tableView dequeueReusableCellWithIdentifier:cellID];
if(cell == nil) {
[self.tableView registerNib:[UINib nibWithNibName:[#"CustomReferenceTableViewCell" ] bundle:nil] forCellReuseIdentifier:cellID];
NSArray *nibContents;
CustomReferenceTableViewCell *cell;
nibContents = [[NSBundle mainBundle]
loadNibNamed:nibName owner:self options:NULL];
NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
NSObject *nibItem = nil;
while ((nibItem = [nibEnumerator nextObject]) != nil) {
if ([nibItem isKindOfClass:[CustomReferenceTableViewCell class]]) {
cell = (CustomReferenceTableViewCell *)nibItem;
}
}
}
This method has been working out for me for the past 8 months. I have no reasons why it could cause a problem to you.
Try to set your static NSString *cellIdentifier = #"referenceCell"; above #interface
And change registerNib place from WillAppear to viewDidLoad. Also you can change cell creating
CustomReferenceTableViewCell *cell = (CustomReferenceTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[NSBundle mainBundle]loadNibNamed:#"CustomReferenceTableViewCell" owner:self options:nil] lastObject];
}
Just follow my coding for your app
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"Reuse"];
NSArray *nib=[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
if(cell == nil)
{
cell = [nib objectAtIndex:0];
}
cell.number.text = #"1";
cell.referenceName.text = #"Name";
cell.reference.text = #"The text of the reference";
return cell;
}
Firstly CodingVoldermort's answer helped me get to sort out the issue. I was setting the File's Owner to my custom class which you shouldn't do. This should be left as NSObject.
The reason I thought it needed to be changed was because I couldn't hook up my IBOutlets by holding control and clicking on the tableview image under placeholders and dragging to the appropriate view in the xib.
Instead I needed to go under 'Show the connection inspector' pane and control + click and drag from there to get the connections to work. Then my original posted code would work.
Many thanks for all the responses guys.
I want to use a custom UITableviewCell with UITableView in same xib without creating a UITableViewCell class?
As you can see bellow i set the identifier for UITableViewCell and used it like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
}
But Not Working?
Add a UITableViewCell *customCell property to your view controller (for example, your file ShowUsers.h)...
#property (nonatomic, strong) IBOutlet UITableViewCell *customCell;
and connect it to the custom cell in your xib. Then use the following code in cellForRow (for example, your file ShowUsers.m) :
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NibName" owner:self options:nil];
cell = self.customCell;
self.customCell = nil;
}
Yes you can do as following code
Under ViewDidLoad or ViewWillAppear
label1Array=[NSMutableArray arrayWithObjects:#"A",#"B",nil];
label2Array=[NSMutableArray arrayWithObjects:#"C",#"D",nil];
label3Array=[NSMutableArray arrayWithObjects:#"E",#"F",nil];
UITableViewDelegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"aCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UILabel *label1=[[UILabel alloc]init];
label1.frame=CGRectMake(0,0,40,40);
label1.text=[label1Array objectAtIndex:indexPath.row];
................
[cell.contentView addSubView: label1];
UILabel *label2=[[UILabel alloc]init];
label2.frame=CGRectMake(50,0,40,40);
label2.text=[label2Array objectAtIndex:indexPath.row];
[cell.contentView addSubView: label2];
UILabel *label3=[[UILabel alloc]init];
label3.frame=CGRectMake(100,0,40,40);
label3.text=[label3Array objectAtIndex:indexPath.row];
[cell.contentView addSubView: label3];
}
Hope this Helps !!!
See..You can do like this. Here you are adding two UITableViewCell in one TableView using XIB.
In Class.h file :-
Declare one mutable array for number of cells.
{
NSMutableArray * sectionRows;
}
#property (nonatomic,retain) IBOutlet UITableViewCell *addTvc1;
#property (nonatomic,retain) IBOutlet UITableViewCell *addTvc2;
In Class.m :-
#synthesize addTvc1, addTvc2;
- (void)viewDidLoad
{
[super viewDidLoad];
sectionRows = [[NSMutableArray alloc] init];
[sectionRows addObject:[[NSMutableArray alloc]
initWithObjects:addTvc1, nil]];
[sectionRows addObject:[[NSMutableArray alloc]
initWithObjects:addTvc2, nil]];
}
In UITableViewDelegate Method -
Add This -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sectionRows count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
{
return [[sectionRows objectAtIndex:section] count];
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
return [[sectionRows objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row];
}
Along with code, In Class's XIB drop and drag two UITableViewCell which should be outside from the view and add those cells with Files Owner.
It will work perfectly. Here no need to create one separate custom UITableViewCell class.
I would like to know where to enter custom code to change the value of a Label property for a UITableViewCell.
I am not sure how this is loaded, as I have put an NSLog in the ViewDidLoad and (id)initWithStyle instance methods but neither write to the log.
I have setup a NIB and custom class all correctly linked, and the Label is linked as a property and no longer causes an error. But I am unable to setText.
This is how the custom cell is called:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
LeftMenuTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"LeftMenuTableViewCell" owner:nil options:nil];
for (UIView *view in views) {
if([view isKindOfClass:[UITableViewCell class]])
{
cell = (LeftMenuTableViewCell*)view;
}
}
}
return cell;
}
This is the code in the IMP file for the LeftMenuViewCell class.
-(void)viewDidLoad {
displayName.text = [self.user objectForKey:#"displayName"];
I can set the displayName to a string and this does not change either. If I add an NSLog to the viewDidLoad for the custom cell class it is not shown, like it is not loaded, but the cell is loaded...?
Without code specifics, I can only give a vague'ish answer.
Your custom cell will need to subclass UITableViewCell, and you need to provide your table with this custom subclass when the data source method tableView:cellForRowAtIndexPath:.
I'd suggest reading up on how cells are added/used with UITableViews:
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7-SW1
Let's say that you have custom UITableViewCell with UILabel called testLabel. If your NIB and custom class are properly linked than you can use following code:
MyTableViewCell.h
#interface MyTableViewCell : UITableViewCell
#property (nonatomic, assign) IBOutlet UILabel *testLabel;
#end
cellForRowAtIndexPath in your UITableViewController or UIViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyTableViewCellId";
MyTableViewCell *cell = (MyTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyTableViewCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
[cell.testLabel.text = [_dataSource objectAtIndex:[indexPath row]]];
return cell;
}
Hope it helps :)
For example
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RouteCell *routeCell = [self.tableView dequeueReusableCellWithIdentifier:routeIdentifier];
if (routeCell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"RouteCell" owner:nil options:nil];
routeCell = [nib objectAtIndex:0];
}
routeCell.travelTime.text = #"Here you are setting text to your label";
return routeCell;
I searched too much, tried lot of thinks but i couldn't make it work.
I have a view controller, and in that controller i have a tableview which has custom cell.
I connect outlets,datasource and delegate but when i run the project, instead of my custom cells. UITableCell is shown and not showing data either. here is my .h and .m files
#import <UIKit/UIKit.h>
#class SelectMutantCell;
#interface MutantViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>{
UITableView *tableView;
IBOutlet SelectMutantCell *mutantCell;
}
#property (nonatomic, weak) IBOutlet UITableView *tableView;
#property (nonatomic, weak) SelectMutantCell *mutantCell;
- (IBAction)cancel:(id)sender;
#end
and here is my .m file
#implementation MutantViewController
#synthesize tableView = _tableView;
#synthesize mutantCell = _mutantCell;
NSArray *mutants;
- (void)viewDidLoad
{
[super viewDidLoad];
//mutants = [mutants initWithObjects:#"Keko",#"HEHE",#"PEPE", nil];
UINib *cellNib = [UINib nibWithNibName:#"SelectMutantCell" bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:#"SelectMutantCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"SelectMutantCell";
SelectMutantCell *cell = (SelectMutantCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSLog(#"---");
cell.nameLabel.text = #"Hehe"];
cell.descriptionLabel.text = #"hhe";
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 3;
}
the NSLog(#"---") is for debugging and it appears when running. the problem is in
cell.nameLabel.text = #"Hehe"];
cell.descriptionLabel.text = #"hhe";
this block, if i write cell.textLabel.text = "test" it works but i need my custom cell to shown.
any help will be appreciated..
Insert if(cell==nil){} loop
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"SelectMutantCell";
SelectMutantCell *cell = (SelectMutantCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[SelectMutantCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"---");
cell.nameLabel.text = #"Hehe"];
cell.descriptionLabel.text = #"hhe";
return cell;
}
Looking at your cellForRowAtIndexPath: method, you never initialize the cell. Try doing it like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"SelectMutantCell";
SelectMutantCell *cell = (SelectMutantCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"SelectMutantCell" owner:nil options:nil];
for(id currentObject in topLevelObjects) {
if([currentObject isKindOfClass:[SelectMutantCell class]]) {
cell = (SelectMutantCell *)currentObject;
break;
}
}
}
NSLog(#"---");
cell.nameLabel.text = #"Hehe"];
cell.descriptionLabel.text = #"hhe";
return cell;
}
I had the same issue. To solve it check the followings :
in your nib file, you have removed the root view created by default, and dragged a UITableViewCell from XCode object browser, then set the custom type of the UItableViewCell to your subclass SelectMutantCell
you have linked the outlets
you have added the cell identifier to SelectMutantCell, as you would do in storyboard
last but not least, you didn't add the same cell in your storyboard TableView, it will conflit between the one from storyboard and the one from [self.tableView registerNib:cellNib forCellReuseIdentifier:#"SelectMutantCell"];
This is a way i invented for loading custom cells
1) i use my UITableViewCell class extension
//.h
#interface UITableViewCell (Extended)
+ (id) cellWithClass:(Class)class;
+ (id) cellWithClass:(Class)class fromNibNamed:(NSString *)nibName;
#end
//.m
+ (id) cellWithClass:(Class)class
{
return [UITableViewCell cellWithClass:class fromNibNamed:NSStringFromClass(class)];
}
+ (id) cellWithClass:(Class)class fromNibNamed:(NSString *)nibName {
NSArray * nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
NSEnumerator * nibEnumerator = [nibContents objectEnumerator];
NSObject * nibItem = nil;
while ((nibItem = [nibEnumerator nextObject]) != nil) {
if ([nibItem isKindOfClass:class]) {
return nibItem;
}
}
return nil;
}
2) create custom UITableViewCell subclass, with a .nib of the same name (CustomCell.xib) where i have all the outlets connected
#interface CustomCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel * labelSmth;
- (void) setupWithTitle:(NSString *)title;
#end
2) in the CustomCell.xib using Interface builder i drag a UITableViewCell and make it class of CustomCell (with reuse identifier CustomCell)(i dont set the File owner)... and than do the UI styling, connect outlets etc...
3) than load it like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * identifier = #"CustomCell";
CustomCell * cell = [self.tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [UITableViewCell cellWithClass:[CustomCell class]];
}
[CustomCell setupWithTitle:[self.titles objectAtIndex:[indexPath row]]];
return cell;
}
*IS THIS APPROACH OK? This worked for many projects, but iam not sure about the reuseidentfier and about the fact if the cell gets proper reused... *
iam also not sure about this
NSArray * nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
as i pass owner self in a class method...
also Apple has came up with
- (void) registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)reuse;
how could this fit my approach?
and also how to use a custom reuse identifier, like if i wanted a method
+ (id) cellWithClass:(Class)class fromNibNamed:(NSString *)nibName reuseIdentifier:(NSString *)reuseIdentifier;
You don't need to invent something new for this. It's been invented for you already. And what you have invented is a common anti-pattern for loading custom cells.
Enumerating the nib contents to get hold of the UITableViewCell in the nib is not the correct approach.
You should define and outlet to your UITableViewCell in the files owner of the nib that you created the UITableViewCell (usually a UIViewController).
Then you can access that cell using this pattern:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"MyCustomCell"; //this should also be specified in the properties of the UITableViewCell in the nib file
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell) {
[[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil];
cell = self.myCustomCellOutlet;
self.myCustomCellOutlet = nil;
}
return cell;
}