Proper way to identify static cells in Xcode? - ios

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.

Related

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

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).

How can I stop the empty "ghost" cells in a table view from dynamically resizing alongside custom cells?

I'm using the typical method of dynamically sizing table view cells that contain text views in them:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
This works just fine for the cells I actually instantiate and use, but it has a strange side effect: the empty cells that the table view shows below instantiated cells end up resizing their height alongside whichever custom cell I'm currently editing.
So when I input three, four, or more lines of text into a newly added custom cell...
...all the other prototype-based custom cells already filled in the
table remain unchanged
...the custom cell being edited resizes dynamically as intended
...but all the "ghost" cells below the last custom cell in the table
view end up dynamically expanding alongside the custom cell that's
being edited
My first thought was that it must have something to do with how I dequeue cells, and specifically what I return when dequeueing doesn't happen:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let myCell = tableView.dequeueReusableCell(withIdentifier: "myCustomCell", for: indexPath) as? CustomCell {
return myCell
} else {
return CustomCell()
}
}
But I tried returning both my custom cell and just UITableViewCell() in the else block and the result is the same.
Does anyone know why this could possibly be happening and/or how to get around it?

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 ;)

dequeueReusableCellWithIdentifier doesn't return cell

I have a static table with one static section. Other sections are dynamic.
I create Table Section and Table Cell for dynamic section. Set identifier for Cell, set custom class for it and even do:
self.tableView.registerClass(UncheckedStoreTableViewCell.self, forCellReuseIdentifier: "StoreCell")
if i don't register it with code, then i get:
'unable to dequeue a cell with identifier StoreCell - must register a
nib or a class for the identifier or connect a prototype cell in a
storyboard'
So when i use this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
return super.tableView(tableView, cellForRowAtIndexPath: indexPath)
}
let cell = tableView.dequeueReusableCellWithIdentifier("StoreCell", forIndexPath: indexPath) as! UncheckedStoreTableViewCell
return cell
}
It works. But if i'm trying to change label: cell.myLabel.text = "one"
or just print(cell.myLabel) got
BAD_INSTRUCTION
You can definitely use dynamic cells in a static table view.
Don't expect a static table view to register your cell's identifier for you. Just do it yourself.
Do you have outlets in the cell class to some view in interface builder? If I were you I wouldn't expect the table view to know about that. It will instantiate your cell class, and that's it. No outlets will be set. I think this is related: load nib in view subclass
By the way, if you've defined a custom .nib for your cell, there's this method: registerNib(_:forCellReuseIdentifier:)
You do not need to register your cell in code.
You have correctly set the identifier of the cell, however this is not enough. In addition to this you also need to open Identity Inspector for your cell and set the class of the cell to be UncheckedStoreTableViewCell. Here is an image showing you where you should set it:
Without this step Xcode will not be able to correctly associate your cell identifier with your custom cell as it doesn't know anything about it!

Can I use a single prototype cell in multiple tableViews?

I am having two different tableviews in two different controller. But the cells, that I need to display in them, look identical. I have created a prototype cell in one tableView and subclassed UiTableViewCell. Now, if I want to use the same cell in a different controller, how can I use it ?
If I just import that customCell file in the new controller and deque it using the same identifier given in the storyboard, it wont work. It says
Assertion failure in -[UITableView
_configureCellForDisplay:forIndexPath:]
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'UITableView dataSource
must return a cell from tableView:cellForRowAtIndexPath:'
So, clearly it means , the cell is nil. So how can I instantiate the same cell from the storyboard ? Is it possible or do I have to create a different customCell for the new table too ?
K.. I got it. First of all,
I can NOT use one prototype cell in two different tableview. But, I
can use the same tableViewCell subclass in two different tableviews.
To achieve it, one just have to copy the prototype cell from one controller and paste it as a prototype cell of the other tableview. Class of the pasted tableview remains the same. Just change the reuseIdentifier. and use it.
Edit:
If your cell has a fairly complicated UI, then it makes more sense to create separate xib for the cell alone. Then programmatically register the xib with the table view. That way, you will have only one copy of the cell and much better at maintaining it when there are changes to the ui.
You can use same prototype cell in different view controllers, you just need to dequeue it from the tableview of controller in which you designed it.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let viewControllerInWhichCellWasDefined = tabBarController?.viewControllers?[0]
let cell = viewControllerInWhichCellWasDefined.tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath)
return cell
}
If you create a custom cell in XIB it should work just fine. However, I suspect the cell's identifier caused the problem. Try to change your cell's identifier for each table view controller.
If it's not, you might want to post the source code
Yes. We can use a prototype cell of ViewControllerA's tableview for tableview of ViewControllerB. Just need to implement the following code in ViewControllerB
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let vc_array = self.navigationController?.viewControllers
let view_controller = vc_array![vc_array!.count - 2] as! ViewControllerA
let cell = view_controller.tableView.dequeueReusableCell(withIdentifier: "ViewControllerA_ID", for: indexPath) as! ViewControllerA
return cell
}
Here ViewControllerA is the ViewController which contains the tableview with prototype cell, and we are using the same cell on the tableview of ViewControllerB
I have used the same UITableViewCell in different apps. just copy one TableViewCell to the other app. That is both apps have the same layout and such. The apps don't belong to the same workspace. The setup works nice, no problems or errors.. just don't know if thats good practice.

Resources