Good morning,
I would like to create a custom design for my prototype cell and I don't have any idea on how to do that. I have tried to follow some guides or tutorials but I always get some error and there is something that I'm not doing right.
At the moment I'm using the default prototye cell to display the following:
http://i.stack.imgur.com/TnYGZ.png
Now I would like to insert a UIImageView, and also 2 UILabels (something like Facebook) with the author and the image displayed in the prototype cell.
How can I do that? Can you show me some example or a good tutorial to follow?
Thanks in advance.
A brief list of things to do in order to achieve your goal is:
1) Create a Cocoa Touch Class File subclass of UITableViewCell
2) Click on storyboard, and change the class of your cell to this one you created
3) Create your IBOutlets for the views you added to your cell (UILabel, UIImageView, etc)
4) Then under cellForRowAtIndexPath, dequeue your customCell and pass data to your views
For example:
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"yourCellIdentifier" forIndexPath:indexPath];
cell.yourImageView.image = [UIImage imageNamed:#"yourImage"];
cell.yourLabel.text = #"Your text";
These are the basic steps to configure your cell, but you can follow this tutorial here since you are a new iOS Developer, and a Video Tutorial comes more handy for you.
Creating a Custom UITableViewCell
Check this tutorial for how to make custom cell with different ways, and this that discuss it in details starting from how to use storyboards.
eridb explained you with IBOutlet. If you want to do that programmatically;
1-) Create new UITableViewCell class. (e.g NewTableViewCell)
2-) Create your properties in NewTableViewCell.h
3-) Create and customize your properties in NewTableViewCell.m like that;
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// create label, uiimageview, etc. here
}
return self;
}
4-) Ok, your custom UITableViewCell class is ready. Now, call it whereever you want to use (e.g NewTableViewController);
#import "NewTableViewCell.h"
5-) Configure your cell in cellForRowAtIndexPath like that;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *reuseIdentifier = #"myTableViewID";
NewTableViewCell *cell = (NewTableViewCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if(cell == nil)
{
cell = [[NewTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
}
cell.yourCustomLabel.text = #"Label-1 text";
cell.yourCustomImageView.image = [UIImage imageNamed:#"test"];
return cell;
}
Related
I have slightly different cells and for such purpose I need to pass parameter from my UIViewController to my subclass of UITableViewCell. But it does not work. The scenario is written as below:
MessagesViewController.m :
#import "MessagesViewController.h"
#import "MessageTableViewCell.h"
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[MessageTableViewCell class] forCellReuseIdentifier:MessengerCellIdentifier];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MessageTableViewCell *cell = (MessageTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:MessengerCellIdentifier];
if (cell == nil) {
cell = [[MessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MessengerCellIdentifier customParam:YES];
}
return cell;
}
MessageTableViewCell.m :
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier customParam:(BOOL)customParam
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// **** GET Custom Parameter (customParam) HERE ??? ****/
}
return self;
}
At this scenario customParam is my parameter. Everything seems as OK, but cell is not nil and so the procedure fails.
Option 1: Remove the dequeueReusableCellWithIdentifier line so that you create a new custom cell every time. Otherwise you are using a pre-existing cell that already has the previous customParameter set to whatever the last cell displayed was set to.
-Note to option 1 (added as explanation of why it is a very, VERY, VERY bad idea (#Duncan C). Since you are setting up your cells with a ReuseIdentifier in creating your cells iOS will hold on to them for you once they scroll offscreen so that you can reuse them when your code asks for it. But then your code never asks for reusable cells because it makes a completely new one each time the table asks for the next cell. This causes high load times (to create a new cell every time) and high memory use (since the OS is saving the cells for you to use later and not deallocating them immediately). The reusability was built for a reason, so don't use Option 1 unless you have very specific need to do so (and even then, you are probably wrong, don't do it).
Option 2: Change the custom parameter to a separate method call. Instead of in the initializer create a new method that clears the cell and rebuilds it the way your new custom parameter requires. Then you can re-use cells and modify their looks using the new setCustomParameter: method.
Edit: Code example of option 2, as simple as possible:
In table controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MessageTableViewCell *cell = (MessageTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:MessengerCellIdentifier];
if (cell == nil) {
cell = [[MessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MessengerCellIdentifier];
}
[cell setCustomParam:customParam];
return cell;
}
In your cell .m
-(void)setCustomParam:(ParamType)type
{
//Do whatever you would like right here to clear the previous
//cell's custom information and add the new custom information
//to this new cell.
}
Then you have to try some another method to add parameters in the MessageTableViewCell. The parameter is nil because the cell aren't nil everytime they are reusing the table view cell from the line
MessageTableViewCell *cell = (MessageTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:MessengerCellIdentifier];
You have to call another method like
MessageTableViewCell *cell = (MessageTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:MessengerCellIdentifier];
if (customParam)
{
// IF custom parameter is your labal
cell.yourCustomParameter.text = #"Add your content here"
}
I am trying to change a cell in a tableview from generic to custom and have created a new tableview cell class. However, I cannot get the cell to recognize the new custom cell. Instead, it is still displaying the generic cell although I've changed the setting in storyboard to custom in the identify inspector and also changed the class for the tableview cell to the new custom one. I have also wired the elements in the cell in storyboard to the new custom class.
My understanding is the tableview VC knows which class to use from the tableView cellForRowAtIndexPath method and I have changed that too with the code below. The project compiles but continues to show old generic cell. Can anyone see error or suggest what else to do?
#import "customCell.h"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
customCell *cell = (customCell *)[self.tableView dequeueReusableCellWithIdentifier:#"Cell"];
Items *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
return cell;
}
Thanks in advance for any suggestions.
Edit:
A custom cell is created in the storyboard on top of tableview by dragging uielements. Maybe this is an issue:
Style of cell is set to Custom:
You need to create new UITableViewCell and enable the "create XIB file...".
Create your custom cell in the XIB and make it's custom class as your class you just created.
Then in tableView cellForRowAtIndexPath register the Nib: (it's in swift but I bet you can figure out the objective c parallel...)
tableView.registerNib(UINib(nibName: "YourUITableViewCellClass", bundle: nil), forCellReuseIdentifier: "YourCustomIdentifierFromTheCustomClass")
Then access your cell:
var cell = tableView.dequeueReusableCellWithIdentifier("YourCustomIdentifierFromTheCustomClass")
and that's it.
you can create custom cell in your main story board by drag the cell view.
create custom class for that prototype.
mention the class name in identifier inspector.
import that class to your view controller .h file.
and made connection of your custom from main story board to custom class.h file.
it works!.
When you create a custom cell you have to do these things:
Setup the labels, images etc on storyboard cell.
Create a custom cell class (inheriting from
UITableViewCell)(CustomCell.h & .m), CustomCell.h having all of the
properties as iboutlet for labels, images etc. and synthesize them all
in implementation.
After creating this custom class go back to storyboard, select the
custom cell, change its class to the CustomCell and give it some
identifier like "MyCustomCell", then right click on the custom cell
and connect the IBOutlets with labels etc.
Now import CustomCell class in the class where you are implementing
the UITableView and use the properties you defined in CustomCell
class.
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath
{
static NSString *MyIdentifier = #"MyCustomCell";
CustomCell*cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the provided setImageWithURL: method to load the web image
// Ensure you use a placeholder image otherwise cells will be initialized with no image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://example.com/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder"]];
cell.myCustomLabel.text = #"My Text";
return cell; }
Select the cell on your Storyboard, and enter the name of your class in this field.
When you dequeue it from the identifier, it'll be an instance of that class.
Please note that each regular tableViewCell has a builtin and hidden UIImageView.
In Objective-C, you simply do this within your cellForRowAtIndexPath,
cell.imageView.image = [UIImage imageNamed:#"My awesome image"];
In Swift, it's pretty close.
cell.imageView?.image = UIImage(named: "My awesome image")
I am having an interesting problem creating the custom tableview I need...
I found this question, but it does not really address my issue...
I am trying to put a subclassed UIView inside a subclassed UITableViewCell. The custom view holds a UIButton and a couple labels. Simplified, it's like this:
Both the custom view and custom tableviewcell have xibs.
MyCustomView.xib's class is set to MyCustomView and I have properties for the labels and the button as well as an IBAction for the button.
MyCustomTableView.xib has a property for MyCustomView and is importing MyCustomView.h.
In MyCustomView.xib, I have this init:
-(id)initWithNibName:(NSString *)nibName nibBundle:(NSBundle *)nibBundle myLabelText:(NSString *)labelText {
//The custom view in the tableviewcell is 69 by 64...
if ((self = [super initWithFrame:CGRectMake(0, 0, 69, 64)])) {
[self setmyLabelText:labelText];
}
return self;
}
And in my TableViewController...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomTableViewCell *cell = (MyCustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"theCell" forIndexPath:indexPath];
// Configure the cell...
cell.customView = [[MyCustomView alloc] initWithNibName:#"MyCustomView" nibBundle:nil fileContentID:#"Some Text..."];
return cell;
}
When I run the app, the custom tableview cell's contents are fine, but the content of the custom view inside the custom tableview cell is blank.
It seems that MyCustomView's initializer(-initWithNibName:nibBundle:myLabelText:) don't load any xib.
This post will help you.
How to load a xib file in a UIView
...and MyCustomView should be created once inside MyCustomTableViewCell, as #rdelmar says.
You need to do most of the formatting work in MyCustomTableViewCell - I would not use a XIB and code the views directly because that class is called many times. Apple has number of sample codes regarding TableViewCells - One of them I believe is called Elements that use fancy tableview cells for the Elements of the Periodic Table. Most of my apps use custom cells with icon images and I started with that sample code many years back (since IOS 4).
Your CellForRowatIndexPath should just be passing the image and the label text to your tableviewCell Class instance. If you have question just ask - but I am sure that sample code from apple is sufficient to get you started.
I am trying to create a "settings" table view for my app. I am trying to mimic it to be the same style as the gneral setting on an Iphone. I have created my own custom cell class by inheriting from UITableCell. I gave it the appropriate IBOulets and i have hooked them up in the storyboard. I also hooked up the switch to my tableViewControler, but for some reason my code is only returning me one empty cell (it being only one cell is not an issue atm for that's all i have in my setting). I triple checked and made sure that I'm using the same cell identifier in my code and in storyboard. Anyone know why I'm getting a blank cell back?
Here is my .h file for my custom cell.
#interface NHPSettingsCell : UITableViewCell
#property (nonatomic,weak) IBOutlet UILabel *settingLabel;
#property (nonatomic,strong) IBOutlet UISwitch *settingSwitch;
#end
MY Problem code is here, my .h file for the custom cell:
#import "NHPSettingsCell.h"
#implementation NHPSettingsCell
#synthesize settingLabel, settingSwitch;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
My method for drawing the cell in my custom view controller:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"SettingsCell";
NHPSettingsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[NHPSettingsCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellStyleDefault;
}
cell.settingLabel.text = #"firstSetting";
//check the if user wants promt of disclaimer each time or not.
if([prefs boolForKey:#"firstSetting"] == YES){
cell.settingSwitch.on = YES;
}else{
cell.settingSwitch.on = NO;
}
return cell;
}
Now the thing that annoys me is i have successfully managed to implement the cellForRowAtIndexPath method for a dynamic table that uses custom cells. I have also implements the code for a static table using the default cell, but for a static table with custom cells it just doesn't seem to work. Here is the code on how I implemented my custom cells on a dynamic table (note how i didn't have to init the cells but it works).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"InteractionResultCell";
NHPResultCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure & Fill the cell
cell.leftLabel.text = [[resultsList objectAtIndex:indexPath.row] substanceName];
cell.rightLabel.text = [[resultsList objectAtIndex:indexPath.row] substanceName2];
NSString *color = [NSString stringWithFormat:#"%#", [[resultsList objectAtIndex:indexPath.row] color]];
//Change a hex value to a readable 0x number to pass ot hte macro so we can go from a hex color to a RGB system.
NSScanner *scanner;
unsigned int tempint=0;
scanner = [NSScanner scannerWithString:color];
[scanner scanHexInt:&tempint];
cell.severityButton.backgroundColor = UIColorFromRGB(tempint);
return cell;
}
Two problems:
If you are using static cells, do not implement any datasource methods in your view controller (numberOfRows, numberOfSections, cellForRow...) as this will override what you have built in the storyboard. The table has the sections, rows and content you give it in the storyboard.
Cells loaded from the storyboard (either dynamic prototypes, or static cells) are initialised using initWithCoder:, not initWithStyle:. awakeFromNib: is a better place to put your set up code.
dequeueReusableCellWithIdentifier: works only if a cell has already been created to prevent repeated memory allocations. You cannot reuse a cell without creating it first. The static cells created in the xib are the default type. That's why it doesn't work for static table with custom cells. Add the cell creation code after reuse as you've done in your custom view controller's cellForRowAtIndexPath: method:
if (cell == nil) {
cell = [[NHPSettingsCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellStyleDefault;
}
EDIT- To init your custom cell, you'll have to load from xib. Add the following class method to your NHPSettingsCell.m:
+(NHPSettingsCell*) createTextRowWithOwner:(NSObject*)owner{
NSArray* wired = [[NSBundle mainBundle] loadNibNamed:#"NHPSettingsCell" owner:owner options:nil];
NHPSettingsCell* cell = (NHPSettingsCell*)[wired firstObjectWithClass:[NHPSettingsCell class]];
return cell;
}
and then call it from your custom view controller as:
cell = (NHPSettingsCell*)[tableView dequeueReusableCellWithIdentifier: CellIdentifier];
if (Nil == cell) {
cell = [NHPSettingsCell createTextRowWithOwner:self];
}
I'd like to create custom UITableViewCell using an XIB, but I'm not sure how to recycle it using UITableViewController's queueing mechanism. How can I accomplish this?
Folks, this question was intended to be self answered as per the FAQ, although I love the awesome responses. Have some upvotes, treat yourself to a beer. I asked this because a friend asked me and I wanted to put it up on StackOverflow. If you have anything to contribute, by all means!
If you're using iOS 5 you can use
[self.tableView registerNib:[UINib nibWithNibName:#"nibname"
bundle:nil]
forCellReuseIdentifier:#"cellIdentifier"];
Then whenever you call:
cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
the tableview will either load the nib and give you a cell, or dequeue a cell for you!
The nib need only be a nib with a single tableviewcell defined inside of it!
Create an empty nib and add the table cell as the first item. In the inspector, you can add the reuseIdentifier string in Interface Builder.
To use the cell in your code, do this:
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *reuseIdentifier = #"blah"; //should match what you've set in Interface Builder
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell == nil)
{
cell = [[[NSBundle mainBundle] loadNibNamed:#"YourTableCellNib" owner:nil options:nil] objectAtIndex:0];
}
//set up cell
return cell;
}
There is another method where you create an outlet for your cell and load the cell nib using the controller as the file's owner, but honestly this is much easier.
If you want to be able to access the subviews you've added to the cell in the nib, give them unique tags and access them with [cell viewWithTag:x];
If you want to be able to set custom properties on the cell, you'll need to create a custom UITableViewCell subclass, then just set that as the class of your nib in InterfaceBuilder and cast the UITableViewCell to your custom subclass when you dequeue it in the code above.
To set up a custom UITableViewCell using a XIB, you have to do several things:
Set up an IBOutlet in your header
Configure the table view cell in Interface Builder
Load the XIB inside of tableView:cellForRowAtIndexPath:
Configure it like any other cell
So... Let's set up an IBOutlet in the header file.
#property (nonatomic, retain) IBOutlet UITableViewCell *dvarTorahCell;
Don't forget to synthesize it inside the implementation file.
#synthesize dvarTorahCell;
Now, let's create and configure the cell. You want to pay attention to the Cell Identifier and the IBOutlet as shown below:
Now in code, you load up the XIB into your cell as shown here:
Notice that the Cell Identifier in Interface Builder matches the one shown in the code below.
Then you go ahead and configure your cell like any other.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"YUOnlineCell" owner:self options:nil];
cell = dvarTorahCell;
dvarTorahCell = nil;
}
//configure your cell here.
Just note that when accessing subviews, such as labels, you now need to refer to them by tag, instead of by property names, such as textLabel and detailTextLabel.
Here how you can do:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
YourCustomeCell *cell = (YourCustomeCell *)[tableView dequeueReusableCellWithIdentifier:CellClassName];
if (!cell)
{
NSArray *topLevelItems = [cellLoader instantiateWithOwner:self options:nil];
cell = [topLevelItems objectAtIndex:0];
}
return cell;
}
Where cellLoader in .h is defined as follow:
UINib *cellLoader;
and in .m is istantiated as follows (for example during initialization):
cellLoader = [[UINib nibWithNibName:CellClassName bundle:[NSBundle mainBundle]] retain];
and CellClassName is the defined in .m as follows (is also the name for your xib).
static NSString *CellClassName = #"YourCustomeCell";
Do not forget to use the string CellClassName also in your xib created cell.
For further info I suggest you to read this fantastic tutorial creating-a-custom-uitableviewcell-in-ios-4.
Hope it helps.
P.S. I suggest you to use UINib because is an optimized method to load xib files.