iOS: custom cell and utility - ios

When I create a tableview with custom cell I use this
static NSString *CellIdentifier = #"mycell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
and it work fine (I work with storyboard and ARC)
but sometimes I see this control:
if (!cell){
//here the alloc of a custom cell
}
should I use this only when I have a custom cell with its class? If I use storyboard do I need it? thanks

dequeueReusableCellWithIdentifier:forIndexPath: will raise an exception if there is no cell to dequeue. So there is no point in checking if cell is nil. There is no way that cell will be nil after this call. You will either have a valid cell or an exception is raised.
That means you have to register a cell (through storyboard or in code) for this call to work.
There is a second (or old) way to dequeue cells, dequeueReusableCellWithIdentifier:, without the forIndexPath:. That's how we did it in earlier versions when there was no way to register cells. If no cell is registered for the identifier this method will return nil, and you have to create a cell in code.
I would stick with registering cells and dequeueReusableCellWithIdentifier:forIndexPath:. You don't need the if (!cell) part in this case. And the exception will make sure that you not forget to register your cells.

If you are using prototype cells with storyboard, you will never get inside that if statement, so you don't need it. Otherwise you do, for example when you instantiate the cell from a xib file, then you would do that inside the if statement.

If you are using storyboards use prototype cells. In this tutorial there is a whole section about protoype cells: http://www.raywenderlich.com/50308/storyboards-tutorial-in-ios-7-part-1

Its not necessary if we are using storyboard.Checkout this link
http://useyourloaf.com/blog/2012/06/07/prototype-table-cells-and-storyboards.html

Related

How to initialize cells with UITableViewController's new behavior in terms of cell reusing

It's been a while that I haven't coded iOS, and I'm witnessing some new behavior and would like to know how it works.
It used to be that cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath] would return a cell or not, but the code examples that I'm seeing now lack the initialization part:
if (cell == nil) {
cell = [MyCellClass new];//and I think somehow registering the cell with the identifier
//Some code here, for example:
//[cell.button addTarget:self action:#selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
}
Is it going to do these initializations every time now, without checking the existence of cell?
Edit: more details about my use case:
My cell has a Nib file and I'm using the new [[self tableView] registerNib:[UINib nibWithNibName:#"cell" bundle:nil] forCellReuseIdentifier:#"cell"] in my ViewController's viewDidLoad. My ViewController is not present in any Nib/StoryBoard.
dequeueReusableCellWithIdentifier:forIndexPath: is guaranteed to return a cell, and always has been (it was added in iOS 6).
You're remembering dequeueReusableCellWithIdentifier: which was used prior to iOS 6, which did not have the same guarantee. The new API is much nicer to use :)
When using registerNib:forCellWithReuseIdentifier: cell creation is taken care of automatically.
From the docs:
Prior to calling the dequeueReusableCellWithReuseIdentifier:forIndexPath: method of the collection view, you must use this method or the registerClass:forCellWithReuseIdentifier: method to tell the collection view how to create a new cell of the given type. If a cell of the specified type is not currently in a reuse queue, the collection view uses the provided information to create a new cell object automatically.

Can I safely use dequed tableView cell as prototype

I am using prototype cell to calculate real cell height. Since I'm using storyboard I have chosen to create prototype cell by dequeing it. Example code:
- (MyCell *)prototypePriceOptionCell
{
if (_prototypeCell == nil) {
_prototypeCell = (MyCell *)[self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
}
return _prototypePriceOptionCell;
}
This prototype is never returned to tableView in cellForRowAtIndexPath method.
Will it be dequed by table view to reuse and show it in table view?
You technically could, from the docs:
If you registered a class for the specified identifier and a new cell
must be created, this method initializes the cell by calling its
initWithStyle:reuseIdentifier: method. For nib-based cells, this
method loads the cell object from the provided nib file. If an
existing cell was available for reuse, this method calls the cell’s
prepareForReuse method instead.
But it also states that it can return nil under some circumstances.
However, I wouldn't use your approach, not because it wouldn't work but because it's not supposed to be used like that. This means that you have weak code relying on a feature it's not supposed to be used like that.
My suggestion, instantiate your cell manually calling the appropriate methods. Also, I like to have my cells on separate xibs, away from storyboards.
Edit:
Answering your question at the end, yes, the cell can and will be dequeued by the table view if you call dequeue in your cellForRowAtIndexPath

Custom tableview cell's xib not showing

So I have a custom uitableviewcell and I have code that looks like this in the cellForRowAtIndexPath method
defaultCell = [self.listView dequeueReusableCellWithIdentifier:DefaultCellIdentifier];
if(defaultCell){
defaultCell = [[DefaultCell alloc]init];
}
The if is passed and the default cell is alloced and inited. However, the cell shows up to be blank (the xib file isn't there). I'm registering the nib with the tableview like this -
UINib* defaultNib = [UINib nibWithNibName:#"DefaultCell" bundle:nil];
[self.listView registerNib:defaultNib forCellReuseIdentifier:DefaultCellIdentifier];
So why am I getting a blank view in my table cell instead of what I see in my xib file? I think it's because I'm not allocing the cell with it's xib.
What's going on?
This call is wrong for two reasons:
if(defaultCell){
defaultCell = [[DefaultCell alloc]init];
}
Firstly, it should be if(!defaultCell)
Secondly, don't even need to check if a cell was dequeued if you've registered a nib for it. dequeueReusableCellWithIdentifier will always return a cell in this case. All you need to do is configure it.
So, actually, you don't even need this little block of code at all.
you first register your nib for the table view cell like so:
[self.tableView registerNib:[UINib nibWithNibName:#"nib name" bundle:nil] forCellReuseIdentifier:DefaultCellIdentifier];
in cellForRowAtIndexPath . you do not need to alloc the cell explicitly.
Instead you can do something like this
defaultCell = [self.listView dequeueReusableCellWithIdentifier:DefaultCellIdentifier];
[defaultCell configureCell];//In configure cell method set any images or labels u want.
return defaultCell
Hope this helps..
UPDATE 1 - correction in code
This is the correct way of showing a custom cell in your cellForRowAtIndexPath method
I learned something today:
you dont need to check if a cell was dequeued or not because the method will create a new cell from the nib if it can't dequeue one. - #Abizern,
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
//Set the label's data properties that you may have assuming you have a datasource.
cell.customLabelICreated.text = [myStringsDataSourceArray objectAtIndexPath:index.row];

How to alter tableview cell text?

I have a UITableView that I want to alter some of the static cells after I do other processing. I have outlets set up for the cells that I want to modify, but when I look at them using NSLog, they show nil, which indicates to me that I don't have the correct cell. For instance, in the image below I want to add the start time to the label just like I did for Date (date was done when creating the cells for which I got the current date),
I tap on the disclosure indicator which takes me to another scene (this was created in Storyboard, using segues to get from one scene to another) where I get the two times I need. I then return to the main scene (shown) and try to alter the Start Time label, but nothing happens. A NSLog of the label prior to trying to alter it returns this:
oStartTimeCell.textLabel.text: (null)
I have read in one of the Apple docs that this textfield is read-only. If that is true in this case, is there a way I can reload the cells with the updated information? Or is there another way to do this?
You're using the wrong approach. You should not create a reference to a cell using an outlet. Once the cell moves out of the visible view, the outlet will either be null or contain garbage data. Even if (in your situation) the cell will never move out of view, I think it shows you're trying to use a UITableView in a way that was not meant to be.
Instead put the data you want to display in your cells in a dataSource, e.g. an array.
The tableView should use the dataSource to configure the values displayed in the textLabels of the cells. Once you want to update the text displayed in the cells, change the values in the dataSource and call reloadData on the tableView to force the tableView to call -tableView:cellForRowAtIndexPath: and related UITableViewDataSource methods.
Try to create an IBOutlet for each cell and connect it:
IBOutlet UITableViewCell *cell1;
IBOutlet UITableViewCell *cell2;
IBOutlet UITableViewCell *cell3;
And also change your method to:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(indexPath.row == 0) return cell1;
if(indexPath.row == 1) return cell2;
if(indexPath.row == 2) return cell3;
if (cell == nil) {
//create cell;
}
return cell;
}
Are you using a UILabel to display the text ? . If you are just create an outlet to the UIlabel and update it any method like cellForRwoAtIndexPath or didSelectRowAtIndexPath etc that is called after you tableView is loaded.
If you are not using a UILabel and just using cell.textLabel you could do something like
cell.textLabel.text = #"ChangedText" in cellForRowAtIndexPathMethod. Make sure you are editing the required cell by checking indexPath.row
Do [tableView reloadData] to call cellForRowAtIndexPath.

Added table view cells follow 1 custom style?

I am wanting to create a custom UITableView cell. I would like to know how to do this. I understand how to actually create it and write code for it, but how can i create 1 style and then when i have more cells added, i want the same style. How can i do this? Is there a way to create 1 custom cell and have all the other cells that i want to add later follow this cells style?Thanks for the help!
In my projects I'm implementing method that creates custom style programmatically. Also it is possible to make custom cell via IB and when you need just take custom cell from it.
Don't forget that if you will write your code correctly then your cells will be reused and that method will be called only for number of cells that are visible in your table view.
may be this can help you http://iphone-bitcode.blogspot.com/2011/06/custom-tableview-cell.html
Write a separate .h/.m/.xib for the cell, and in the .xib set File's Owner to the class you want multiple copies of it in (your table view controller class, most likely). Attach it to an IBOutlet you created in the table view controller for new cells.
Then, each time you want a cell, try and dequeueReusableCellWithIdentifier: on your tableView, and if that doesn't work (you have no reusable ones), make a new cell using your custom class by simply loading the nib file. It will automatically create an instance of the cell and attach it to your IBOutlet, and then just retain the cell and set the outlet back to nil for the next time you need to create a cell. Essentially, I mean this (I have an IBOutlet UITableViewCell *cellOutlet):
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *reuseIdentifier = #"CustomCell";
UITableView *cell = [self.tableView
dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"MyCustomTableViewCell"
owner:self options:nil];
cell = cellOutlet;
self.cellOutlet = nil; // autoreleases
cell.reuseIdentifier = reuseIdentifier;
}
// configure the cell here
return cell;
}

Resources