Why do I need to specify the reuseIdentifier in a UITableViewCell? - ios

I know how to set up custom UITableViews with custom cells, but I don't understand why I need to set the class AND the reuseIdentifier. And because of that, it often leads to scenarios where I am simply naming the reuse identifier with the same name as the UITableview cell class.
As a pratical example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as? HeaderCell
return cell!
}
I am trying to understand why the reuse identifier is even necessary. Is there a scenario where I would use the same class, but have two different reuse identifier names?

Certainly. It's completely legal to have a UITableViewCell that's just a UITableViewCell (not a subclass). And you might configure them different ways, and have them all in the same table. Or your HeaderCell might be a "MyCustomStyleCell" that you use in different places, and just configure it for the header, rather than making a HeaderCell subclass. There's no rule that you have to make a subclass for each kind of cell (it's not even particularly encouraged by UIKit).

Related

Proper way to identify static cells in Xcode?

I have a table view controller and I overrode this function:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
// Configure the cell...
return cell
}
My cell identifier is myCell. I created 4 static cells, and went into each cell and gave each the identifier myCell. However, this crashes due to not recognizing the cell identifier. However, if I change to dynamic prototypes, give the cell the myCell identifier, it works as intended. I guess this will do for now...as I can just tell it dynamically how many cells I want. But I'm really interested to find out why my static cells aren't working with the same exact method. Any ideas?
When you have static cells (i.e. a small, fixed set of static cells, rather than dynamic cells with cell prototypes where you control how many of which types of cells will be dynamically generated), you shouldn’t implement any of the UITableViewDataSource methods. Just create IBOutlet references for the various controls you have in your static cells and update them just like you would if you weren’t using a UITableView at all.
As an aside, if you were using dynamic cells, you would never give two different cell prototypes the same reuse identifier. The purpose of the reuse identifier is to let it know which cell prototype to use, and it wouldn’t therefore make any sense to give multiple cell prototypes the same identifier.

iOS analog for inflating a view from a layout (xib)

I'm new to iOS and trying to rewrite an app from android.
What I do in Android - I have a layout - same as nib in xcode
And then I inflate this view as many times I want - same as ListView (TableView's android analog) is working
I need to do this on iOS :
which means having some container like Android's horisontal LinearLayout where I can add nib's with their class - like UITableViewCell and fill data.
When I asked one person who is iOS developer, he told me that it is almost impossible to do due to compexity and lack of android-like ViewGroups and that it's better to do in a WebView than natively.
So please, tell me , is there a solution - to inflate as many views as needed from a nib into container-views one under another ? Please answer in Swift, I don't know Obj-c at all
Yes you can do it by registering your tableView with Xib in viewDidLoad
tableView.registerNib(UINib(nibName: "CustomCellXib", bundle: nil), forCellReuseIdentifier: "CustomCell")
Then do this in delegate method
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
cell.middleLabel.text = items[indexPath.row]
cell.leftLabel.text = items[indexPath.row]
cell.rightLabel.text = items[indexPath.row]
return cell
}
where CustomCellXib is your Xib file name.
CustomCell is your class of CustomCellXib
and #"CustomCell" is string identifier for reusing cells
Note down that you will have to implement other few delegates methods too for complete working of TableView.
There is nothing extremely complex here. It's a common UITableView with multiple sections.
Red text labels for every sections should be implemented as section headers
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//create a view here
}
Custom UITableViewCell with grey labels, red - and + buttons in it.
Custom cell should contain a UIImageView below it's content. Since it is different for first, middle and last rows, you should set one of three images in cellForRow method depending on the cell's indexPath.row.
P.S. Don't listen to your iOS developer. You should not use UIWebView whenever you need to implement UI a bit more complex than default UITableView. Most probably he was joking ;)

Two TableViews that have same prototype cell, constraints behave differently

I created two ViewControllers and two TableViews. Then i added prototype cell to one TableView, set it up according to my needs, copied it to the other TableView, changed its class and identifier and linked it up in ViewController that is datasource and delegate for each one.
The problem is, FEEDING one is behaving good, having constraints as expected, and the WALKING one is not, but i have no idea why since they have all same properties in each one's:
ViewControllers:
FEEDING
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myFeedingTableView.dequeueReusableCellWithIdentifier("feedingcell", forIndexPath: indexPath) as! FeedingCell
cell.time.text = self.vremena[indexPath.row]
return cell
}
WALKING
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myWalkingTableView.dequeueReusableCellWithIdentifier("walkingcell", forIndexPath: indexPath) as! WalkingCell
cell.time.text = self.vremena[indexPath.row]
return cell
}
CustomCell files
each one is connected to its class
FeedingCell is class of feeding prototype cell
WalkingCell is class of feeding prototype cell
Constraints
and the constraints are same, as you can see on the picture.
Here is the image providing different results and constraints:
image
Solved by changing rowHeight settings in TableView. Thanks #SilentLupin

Should I dequeue cell instead allocate it, if cell is UITableViewCell?

What is a better way:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
return cell
}
or
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier) //cell has empty prototype in storyboard where setted this cellIdentifier
return cell
}
I know dequeueReusableCellWithIdentifier will reuse cells. But it would be better for performance in this case if we use simple UITableViewCell?
You should never use the first method. Performance wise the dequeuing method is much much better.
If you want a cell with a different style or different labels then create that in the storyboard or create a subclass.
But always use the dequeuing method.
Both are absolutely fine. It depends on you what type of setting you want. As far as dequeueReusableCell is consider it just save the memory. It allocates only some number of cell which can easily be shown into you device screen. For example if your screen can view 7 cells it will allocate 8 cells and then try to reuse these cell for different number of objects with respect to their indexPath.

Calling super.tableView(tableView: UITableView, cellForRowAtIndexPath... with reuseIdentifier

First, I wanted to point out the reason I want to make a common call, when a reusable cell is dequeued in a base class, it's because of this line of code you will see again soon further in my question:
cell.backgroundColor = cell.contentView.backgroundColor;
This is a bug fix for iPad not respecting the UITableView.backgroundColor = myCustomColor and UITableViewCell.backgroundColor = clearColor I have set. iPad displays white instead, everything is fine in iPhone versions, you can see this bug here, I have to set the background color again each time the cell is dequeued that is the only solution that works for me. I am trying to do this once in my base class, and come up with a solution where I do not have to remember to call a func for every child class (might not be possible).
I have a couple custom UITableViewControllers classes, let's call them ATableViewController and BTableViewController they inherit from a base class called UIBaseDashboardTableViewController which inherits from UITableViewController.
I am generating dynamic Prototype Table cells and making use of the function below in ATableViewController and BTableViewController:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ATableViewCellId", forIndexPath: indexPath)
//common setting to fix always white background in iPad bug
cell.backgroundColor = cell.contentView.backgroundColor;
return cell
}
The TableViewCell Id ACustomTableCellId is unique or different for ATableViewController and BTableViewController. I have a common setting for all my UITableViewControllers that inherit from my base class, UIBaseDashboardTableViewController. You can see the backgroundColor line of code above is my common setting that will be the same in all child classes of UIBaseDashboardTableViewController. In each child class I first tried to do the following:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
super.tableView(tableView: tableView, cellForRowAtIndexPath: indexPath)
...
}
But that is not going to work, I need the ReusableCellIndentifer.
My current solution, which really is just fine probably, is the following, in my child classes I have the following:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let data = dataArray[indexPath.row]
let dequeuedCell = tableView.dequeueReusableCellWithIdentifier("BTableViewCellID", forIndexPath: indexPath)
let cell = dequeuedCell as! MyCustomTableViewCell
// Configure the cell...
cell.model = data
//call common settings for cells function in base class
super.setDequeueReusableCellCommon(cell)
return cell
}
And then in my base class UIBaseDashboardTableViewController I implemented:
func setDequeueReusableCellCommon(cell: UITableViewCell) {
cell.backgroundColor = cell.contentView.backgroundColor
}
The only downside to this is that I have to remember to call super.v setDequeueReusableCellCommon in all my child classes.
Any better suggestions on how solve this?
You are changing background color for cell, but made inheritance for tableViews. Use inheritance for tableViewCell, not the whole tableView. And in root class for tableViewCell setup self.backgroundColor = self.contentView.backgroundColor in awakeFromNib method.

Resources