Creating custom UITableViewCell's within a Storyboard - iphone

Wanting to create a static menu (IOS 5) and attempting to create custom cells within the storyboard to then load onto the grouped tableview.
I've created the outlet
#property(nonatomic,strong) IBOutlet UITableViewCell *labelCell;
The ViewController class is set to the proper TableViewController and I've connected the custom cell to this outlet.
I also have the delegate and datasource set up.
I've got
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return self.labelCell;
}
I'm sure there is a ton wrong with this, but I'm just trying to display one cell and go from there. There does not seem to be any examples of doing custom cells within the IB through the storyboard. I can still use the old way of creating a xib file and loading it in the mainBundle but I just want to stay up to date I guess.
but with what I have above i get a crash when I load this view controller. SIGABRT

Here is what I've learned about how you get cells for your table when using the storyboard. When you drag a UITableView into your view, it comes with a prototype cell already set as a subview. To use this prototype cell, set a unique reuse identifier in the attributes inspector, and then use the same identifier to dequeue the cell in your cellForRowAtIndexPath: method. I leave out the code for creating a cell from scratch if the dequeue call returns nil; I don't think it can happen. So just dequeue the cell, configure it with the usual UITableViewCell methods, and return it.
But you can also create custom subclasses of UITableViewCell. Just set the class name in the storyboard's class identity inspector, and drag whatever elements you want from the Objects palette into your cell. Then create IBOutlet properties for them in your subclass's code files, and hook them up to the cell in the storyboard in the usual way. This is so much better than having to do it all in code!
And finally, you can have more than one kind of cell in your table. Just drag UITableViewCell objects from the palette into the table, and give each one a unique reuse identifier in the attributes inspector. In your cellForRowAtIndexPath: method, choose the type of each cell and you can have a very flexible table view.

If you have set your UITableView to be using 'Static Cells' in the storyboard, you don't need to implement any of the UITableViewDataSource methods and you can modify the cell directly in Interface Builder. For a single label cell, select the cell and change it's type to 'Basic'. You can now edit the cell just like you would any other view object.

This tutorial was helpful to me. You can reference whatever object you need through the tag.
In the Storyboard drag on a UIImageView or UILabel etc. and set the tag to 100 (whatever you want) then in your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath use the tag to reference it.
Here is the example code in the tutorial, just remember to set the tags in the storyboard:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Display recipe in the table cell
Recipe *recipe = [recipes objectAtIndex:indexPath.row];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:recipe.imageFile];
UILabel *recipeNameLabel = (UILabel *)[cell viewWithTag:101];
recipeNameLabel.text = recipe.name;
UILabel *recipeDetailLabel = (UILabel *)[cell viewWithTag:102];
recipeDetailLabel.text = recipe.detail;
return cell;
}

Related

Prototype cells different from dynamically created cells

I have created two types of prototype cells in storyboard. The dimension of one of them have been customized to accomodate UIButton object. However when the cells are created, they have the standard height. I can see the UIButton object but it gets truncated because of the cell height.
Why are the newly created cells different from the prototype cells?
The relevant section of the code is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if(cell == nil)
{
cell = [tableView dequeueReusableCellWithIdentifier:#"PictureSelectionCell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
pictureButtonProperty = (UIButton *) [cell viewWithTag:1];
}
}
Going forward, what are my options for creating the cell of the width (or dimensions) defined in the storyboard? Programmatically, I will be able to achieve this by creating a CGRect object with the specified dimensions and then create a cell using initWithFrame. However, I would like to avoid doing things manually.
Thanks for your response.
first of all you can always set it with code
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return YOUR_ROW_HEIGHT;
}
other way if you choose your UITableView if the storyboard, under the size inspector change the Row Height.

registerNib:forReuseidentifier with custom UTTableViewCell and Storyboards

I'm migrating from customizing my TableViewCells in tableView:cellForRow:atIndexPath: to using a custom UITableViewCell subclass. Here's how I done it:
First, created empty XIB, dragged UITableViewCell there and put a UILabel on top. Created a class (subclass of UITableViewCell) and in Interface Builder's properties editor set the class to MyCell.
Then, in my TableViewController, put the following:
- (void)viewDidLoad
{
[super viewDidLoad];
// load custom cell
UINib *cellNIB = [UINib nibWithNibName:#"MyCell" bundle:nil];
if (cellNIB)
{
[self.tableView registerNib:cellNIB forCellReuseIdentifier:#"MyCell"];
} else NSLog(#"failed to load nib");
}
After that I wiped out all the custom code from tableView:cellForRow:atIndexPath: and left only default lines:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
return cell;
}
When I ran this, I expected to see a bunch of cells with a single label in each cell (the very label that I dropped in the middle while creating XIB). But instead I see just plain white empty cells and adding/removing the components to the XIB layout doesn't help.
I spend A DAY trying different options like setting the reuseIdentifier in Interface Builder for custom cell XIB and loading the view in tableView:cellForRow:atIndexPath:, but none helped.
...but it turned out, that the only thing that I missed, was clearing the reuseIdentifier for prototype cell in my Storyboard for this TableViewController. It seems that Storyboard initializes its views/components later that viewDidLoad called, and instead of taking my nice custom cell, xCode sets the real cell view for reusing to just plain white cell which is the standard for newly created TableViewControllers.
So again: go to your TableView properties and remove the reuseIdentifier you set before ;)
I spend so much time for this, so I thought it might help someone if I share this experience here.

How to control a customized UITableViewCell within a UITableView Implemented within a UIViewController?

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 to display a complex UIViewController inside another UIView Controller?

Ok, so here's the situation. I currently have a view controller called MainViewController which has a UITableView with many different cells. When I click on a cell, I want that cell to expand (grow in height) and show some "additional information". The problem is, this additional information is very complex and can contain UILabels, other UITableViews, UIWebViews and UIImageViews. Furthermore, this "additional data" requires quite a bit of computation in order to determine what exactly to display (i.e. what the UILabels say, how large the UIImageViews are). Therefore, because of the complexity of this "additional information", I'm at a loss as to how to design my program.
The "additional information" requires a lot of code, thus I don't want to just throw that code into the MainViewController. Additionally, it would be nice if there was some way to use Interface Builder to design these "additional information" views graphically rather than programatically.
Currently I have each set of additional information as its own separate UIViewController (thus allowing me to have separate classes for the data and allowing me to use interface builder) and I just segue to a new screen when a cell is selected. However, I don't want to segue to a new screen; I want all of the data that this UIView controller is showing to be shown in MainViewController. What's the best way to do this?
In summary, I currently have one UIViewController segueing to another UIViewController; however, I want the second UIViewController's content to be show in the first. If possible I would like to use some sort of Interface Builder and to separate out the logic for this second UIViewController into another class.
Details:
~ I'm developing for iOS 5 only and I'm using ARC.
~ I've never developed for iOS 4 or below before and I have never used nib files before but I would be willing to learn if required. Simple sample code would be helpful.
~ Thanks!
Same opinion as SmartWork.
You should create your custom UITableViewCell class with its xib file, with a UITableViewCell as main xib view
And in your tableView datasource, you can import it as below :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MyViewCell";
MyViewCell *cell = (MyViewCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyViewCell" owner:nil options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (MyViewCell *)currentObject;
break;
}
}
}
[self configureCell:cell atIndexPath:indexPath]; // your own function to customize your cell.
return cell;
}
Then, in the cell xib, you can set the max height of the cell, and decide the effective height in the UITableViewDelegate class.
There are some good suggestions here, but note that loadNibNamed:owner: is a fairly expensive API to call repeatedly because it reads the nib from the filesystem each time. Instead, consider doing something like this.
Register your nib file in viewDidLoad. In Interface Builder, make sure to provide a reuse identifier for your custom cell.
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *myNib = [UINib nibWithNibName:#"MyNib" bundle:nil];
[self.tableView registerNib:myNib forCellReuseIdentifier:#"MyCell"];
// Do any other stuff you need to do...
}
Then just dequeue your custom cell whenever you need it. UINib will cache the nib file in memory to avoid reloading it from the filesystem each time.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
// Configure cell as needed...
return cell;
}
in my opinion there is no need of using one UiviewController inside another.
u can use Uiview with nib file so u can design these "additional information" views graphically. its very easy to implement and maintain it.
Just to add to what SmartWork said, when you tap a particular cell, you can update the height of that row using the following lines of code:
- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// set a dynamic value for the cell height depending on the state of the data in the cell
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// update the state of the data in the cells here..
// calling these below lines will change the height of the cells smoothly
[tableView beginUpdates];
[tableView endUpdates];
}
You will also need custom UITableViewCells. Look at them as simple views and add and remove any number of subviews that you need
If you are keen on using Nibs for your subviews inside the cells, you can create their nibs and connect them to your Custom TableView Cells as follows: (The subviews can be properties of your tableViewCell)
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"MyTableViewCellNib" owner:self options:nil];
mySubview = [(SubView *)[nib objectAtIndex:0]retain];

Reusable TableViewCell in Interface Builder WITH changeable labels?

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.