dequeueReusableCell returns empty cell after table initialization - ios

I have a table view that is initialized with three rows when loaded. Later, I want to extract cell data (from labels) and use it when that specific cell is selected by the user.
Even though initialization works well and I can see all the data being displayed, the dequeueReusableCell in the didSelectRowAt methods returns empty cells, with no data.
What is the problem?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? ArticleTableViewCell else {
fatalError("Whoopse, dequeueing the cell failed")
}
let title = cell.articleLabel.text
// Do other stuff
}
The title variable above will be empty, even though it's data is shown on the display.

Replace
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? ArticleTableViewCell else {
fatalError("Whoopse, dequeueing the cell failed")
}
with ( not recommended )
guard let cell = tableView.cellForRow(at:indexPath) as? ArticleTableViewCell else {
fatalError("Whoopse, dequeueing the cell failed")
}
but ##better## is to access the data source array with the clicked index
let item = arr[indexPath.row]

Related

Is there any advantage doing a guard let on a table view cell or not?

I have seen people writing this code inside a table view delegate
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! SuitCell? else {
fatalError()
}
...
}
now consider this other code
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! SuitCell
...
}
Won't both codes crash at the same lines if the cell is not dequeued?
Is there any difference? I am not seeing it.
dequeueReusableCell(withIdentifier:) can return nil in the case where there are no cells in the re-use pool (i.e. When the tableview is first shown). When it returns nil it is your responsibility to instantiate a cell of the appropriate type.
Therefore, this block of code:
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! SuitCell? else {
fatalError()
}
says "If you get a cell from the re-use pool and it isn't an instance of SuitCell, crash, but nil is OK" (Note the cast to an optional)
While this block of code:
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! SuitCell
Says "Crash if you don't get an instance of SuitCell, or youn get nil", so this will crash when the tableview is first shown.
dequeueReusableCell(withIdentifier:) isn't really used any more. You would use the newer (but still been around since iOS 6) dequeueReusableCell(withIdentifier:,for:) variant as it always returns a cell and you can expect it to be the right class (or you will quickly find your problem during development):
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) as! SuitCell

Loading table view with data with 2 different prototype cells

I am trying to populate a tableView with different prototype cells at different times. The problem is any code after let cell = tableView.dequeueReusableCell(withIdentifier: "MeArticlesCell") as? MeArticlesCell else {return UITableViewCell()} isn't being called and I can't figure out why. Im returning the correct amount of rows.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(postsSelected){
guard let cell = tableView.dequeueReusableCell(withIdentifier: "messageCell") as? feedMessagesCell else {return UITableViewCell()}
//the code in this part loads posts fine
} else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "messageCell") as? feedMessagesCell else {return UITableViewCell()}
//this part never gets called
}
}
Basically your guard statement fails and goes into the else part.
guard let cell = tableView.dequeueReusableCell(withIdentifier: "messageCell") as? feedMessagesCell else { // this fails
// enters here
return UITableViewCell()
// exist the function - nothing will be run after
}
My guess is it fails to cast your it as a feedMessagesCell, so tableView.dequeueReusableCell(withIdentifier: "messageCell") as? feedMessagesCell makes cell to be nil ( and then enter the else part )
You're using dequeueReusableCell(withIdentifier:) method to init cell which return nil if it doesn't find any reusable cell. More here
A UITableViewCell object with the associated identifier or nil if no
such object exists in the reusable-cell queue.
You should use dequeueReusableCell(withIdentifier:for:) which init a new cell if it doesn't find any cell for reuse. More here
A UITableViewCell object with the associated reuse identifier. This
method always returns a valid cell.

How to change data in cell in dynamic UITableView xcode project?

I have a Dynamic Prototypes Table view that has different cells, I'm adding the cells to the table view and I want to change their content. All the tutorials I find is for a tableview with only one type of cell, but I have 8 different types. How would I change their content (ie, textfields etc) and how would I get actions from them back to the main tableview controller to do some business logic? (ie button pressed etc)
What I did is:
I created a costume class for each cell type and connected them under customClass, class field.
I attached the textfields etc, actions and references to these classes.
this is my cellAtRow function I assume I would change it in this function somehow?
or reference the classes from here?
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print ("indexPath: ", indexPath)
print ("indexPath: ", indexPath[0])
print ("-------")
if (sectionsData[indexPath[0]] == "header") {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "description") {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerInfoCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "diagnoses") {
let cell = tableView.dequeueReusableCell(withIdentifier: "diagnosisCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "perscription") {
let cell = tableView.dequeueReusableCell(withIdentifier: "perscriptionCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "notes") {
let cell = tableView.dequeueReusableCell(withIdentifier: "notesCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "addFaxHeadline") {
let cell = tableView.dequeueReusableCell(withIdentifier: "addFaxCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "addFax") {
let cell = tableView.dequeueReusableCell(withIdentifier: "emailNameCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "addEmailHeadline") {
let cell = tableView.dequeueReusableCell(withIdentifier: "addEmailCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "addEmails") {
let cell = tableView.dequeueReusableCell(withIdentifier: "emailNameCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "givePermissionHeadline") {
let cell = tableView.dequeueReusableCell(withIdentifier: "permissionCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "select answer") {
let cell = tableView.dequeueReusableCell(withIdentifier: "selectAnswerCell", for: indexPath)
return cell
}
You need to use
let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath) as! HeaderTableViewCell
to call cell.yourTextField.text for example
You have to cast your cells to the class they belong. On the second line of the code block you can see an example of this.
if (sectionsData[indexPath[0]] == "header") {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath) as! HeaderTableViewCell
cell.titleLbl.text = "Title"
cell.delegate = self // To receive actions back
return cell
}
. . . // More of the same
// default return
To send calls back to your main view controller you can add protocols to your cells like so:
protocol HeadTableViewCellProcol{
func bttnPressed()
}
class HeadTableViewCell: UITableViewCell{
var delegate: HeadTableViewCellProcol?
#IBAction func bttnPressedInCell(){
delegate?.bttnPressed()
}
}
The this of this protocols like the protocols you had to implement for your UITableView. You will also have to implement these protocols in your main VC.
You need to cast UITableViewCell into your dynamic cell class. You can try the following:
guard let cell = tableView. dequeueReusableCell(withIdentifier: "perscription", for: indexPath) as? PerscriptionTableViewCell else { return UITableViewCell() }
cell.setupCell() //You have access to cell's public funcs and vars now
return cell
Using optional unwrapping, you can be sure that your app is likely to be safe from type casting crashes.
As the Apple documentation says, the return type of the dequeueReusableCell is a UITableViewCell.
Apple Documentation
Return Value: A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
Your custom cells classes should inherit from UITableViewCell and to be able to use an instance of your custom cell you need to cast the returning UITableViewCell of the dequeReusableCell into your desire custom cell type.
let cell = tableView.dequeueReusableCell(withIdentifier: "customCellIdentifierCell", for: indexPath) as! YourCutsomTableViewCell
For customizing, every cell is responsible for his own configuration. You should have a function (you can use protocols or inherit from a superclass) and inside the cellForRowAtIndexPath, after casting it, call the setup function.
customCell.setup() //you can add some parameters if its needed

swift Non-void function should return a value in a guard let

guard let cell = tableView
.dequeueReusableCell(withIdentifier: cellIdentifier) as? FolderAndFileCell else {
print("some")
return
}
It says that
Non-void function should return a value
What should I return here?
Inside cellForRowAt you have to
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? FolderAndFileCell else {
print("some")
return UITableViewCell()
}
This signature
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
should has a non void return value
The well-know approach is
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! FolderAndFileCell
This is an instance where you should be doing this instead.
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! FolderAndFileCell
You shouldn't need to use guard because I assume you are wanting to set the cell to FolderAndFileCell. If you can't set the cell to that, and go ahead and return a UITableViewCell() you are going to just get an empty cell and not know the reasoning.
I suggest you force cast to FolderAndFileCell and deal with the error if it presents then simply returning an empty cell if an error setting the cell is present.

tableView.cellForRow(at: indexPath ) always nil?

I never get this print . it is like is always nil, doesnt matter how much i scroll it up or down :/
if let update = tableView.cellForRow(at: indexPath ) as? RestCell {
print("VISIBLE CELL")
}
Complete code..
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : RestCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! RestCell
// Pass data to Cell :) clean the mess at the View Controller ;)
cell.restData = items[indexPath.row]
// Send cell so it can check update the image to the right cell ;)
// cell.cell = tableView.cellForRow(at: indexPath ) as? RestCell
//print("LA CELDA ES \(tableView.cellForRow(at: indexPath ))")
if let update = tableView.cellForRow(at: indexPath ) as? RestCell {
print("VISIBLE CELL")
}
return cell
}
You have not returned your cell from cellForRow method so this is the reason why your if statement is returning false. I'm not sure why you are trying to do this but there have to be a better way. If you want to look for visible cells you can use UITableView visibleCells variable.

Resources