Dequeue multiple cells conforming to same protocol - ios

I have a table view with 2 different cells. Both cells conform to same protocol "WorkoutCellProtocol" and I want to avoid rewriting same code during dequeuing. Probably there will more cells in the future, but each will conform to same protocol.
First cell is WorkoutCell with identifier: "WorkoutTableViewCell"
Second cell is CardioCell with identifier: "CardioTableViewCell"
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: WorkoutCellProtocols!
cell.delegate = self
cell.editableRowBorders = colorEditable
cell.numberOfCell = indexPath.row
cell.numberOfExercise = indexPath.section
cell.configureTextFields(model:
exercises[indexPath.row])
if data[indexPath.row].category == "Cardio" {
cell = tableView.dequeueReusableCell(withIdentifier: "CardioTableViewCell", for: indexPath) as! CardioTableViewCell
return cell as! CardioTableViewCell
} else {
cell = tableView.dequeueReusableCell(withIdentifier: "WorkoutTableViewCell") as! WorkoutTableViewCell
return cell as! WorkoutTableViewCell
}
}
When I try to do this in this way, so assign properties only once at the top, before assigning a class types I get "Unexpectedly found nil while implicitly unwrapping an Optional value".

Dequeue your cell first, then configure it:
var cell: WorkoutCellProtocols
if data[indexPath.row].category == "Cardio" {
cell = tableView.dequeueReusableCell(withIdentifier: "CardioTableViewCell", for: indexPath) as! WorkoutCellProtocols
} else {
cell = tableView.dequeueReusableCell(withIdentifier: "WorkoutTableViewCell") as! WorkoutCellProtocols
}
cell.delegate = self
cell.editableRowBorders = colorEditable
cell.numberOfCell = indexPath.row
cell.numberOfExercise = indexPath.section
cell.configureTextFields(model: exercises[indexPath.row])
return cell
I'm assuming that your protocol is declared like this:
protocol WorkoutCellProtocols: UITableViewCell {
...
}

You can't do what you are trying to do with any implicitly unwrapped optional. Try moving the assignments down below the if block and type the cell to the protocol like this
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: WorkoutCellProtocols!
if data[indexPath.row].category == "Cardio" {
cell = tableView.dequeueReusableCell(withIdentifier: "CardioTableViewCell", for: indexPath) as! CardioTableViewCell
} else {
cell = tableView.dequeueReusableCell(withIdentifier: "WorkoutTableViewCell") as! WorkoutTableViewCell
}
guard let workoutCell = cell as WorkoutCellProtocols! else { fatalError("Unexpected cell type") }
workoutCell.delegate = self
workoutCell.editableRowBorders = colorEditable
workoutCell.numberOfCell = indexPath.row
workoutCell.numberOfExercise = indexPath.section
workoutCell.configureTextFields(model: exercises[indexPath.row])
return workoutCell
}

Related

UItableview methods not being called in a XIB

I am trying to use a three tableviews in a XIB. I've created a custom UITableViewCell class, however when I am trying to use it, comes up with an error saying..."Unexpectedly found nil while unwrapping an Optional value". I have tried regist
internal func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell{
if tableView == resultsTable {
if savedWheelsArray.count > 0 {
self.resultsTable.register(accessoryCellTableViewCell.self, forCellReuseIdentifier: "accessoryCell")
let cell = tableView.dequeueReusableCell(withIdentifier: "accessoryCell" , for: indexPath) as! accessoryCellTableViewCell
//let wheel = savedWheelsArray[indexPath.row]
cell.AccessoryTitle.text = "WTF"
cell.AcessoryImage.image = UIImage(named: "history")
print("set up searched wheels cell")
return cell
}
else {
let cellIdentifier : String = "accessoryCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! accessoryCellTableViewCell
cell.AccessoryTitle.text = "no saved wheels"
cell.AcessoryImage.image = UIImage(named: "history")
print("no saved wheels found")
return cell
}
}
else {
}
return UITableViewCell()
}

Multiple Tableviews inside Collection-view

I am working on Multiple Tableviews inside Collection-view. And I followed this article. But inside the cellForRowAt indexPath method I am getting following error.
Value of type 'UITableViewCell' has no member 'lblLab'
Value of type 'UITableViewCell' has no member 'lblLab'
Value of type 'UITableViewCell' has no member 'lblMedicine'
I have already created the separate classes for all the three tableview cells in which all the three labels are already mentioned.
Below is my code for the same which is written inside collection-view cell class.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell : UITableViewCell;
if tableView == tableLAB {
cell = tableView.dequeueReusableCell(withIdentifier: "labCell", for: indexPath) as! LabTableCell;
cell.lblLab!.text = arr1[indexPath.row];
} else if tableView == tableDIAGNOSIS {
cell = tableView.dequeueReusableCell(withIdentifier: "diagnosisCell", for: indexPath) as! DiagnosisTableCell;
cell.lblDiagnosis!.text = arr1[indexPath.row];
} else if tableView == tableMEDICINE {
cell = tableView.dequeueReusableCell(withIdentifier: "medicineCell", for: indexPath) as! MedicineTableCell;
cell.lblMedicine!.text = arr1[indexPath.row];
}
return cell;
}
can you tell me what I am doing wrong? Thanks in advance.
Problem is you are trying to force cast an object initialised through parent class into a child class, this is not possible, what you can do is make sure that you have conditional operators for all cases i.e
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(tableview == a){
//This is a conditional statement
}
else if(tableview == b){
//This is a conditional statement
}
else{
//This is a conditional statement
}
//no need to return anything here as your conditional operators are handling all return //cases
}
and in each of the conditional statement, declare and initialise your unique cell type and return it eg.
let cell : CellType = tableView.dequeueReusableCell(withIdentifier: "CellType", for: indexPath) as! CellType;
return cell;
You can try
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == tableLAB {
let cell = tableView.dequeueReusableCell(withIdentifier: "labCell", for: indexPath) as! LabTableCell;
cell.lblLab!.text = arr1[indexPath.row];
return cell;
} else if tableView == tableDIAGNOSIS {
let cell = tableView.dequeueReusableCell(withIdentifier: "diagnosisCell", for: indexPath) as! DiagnosisTableCell;
cell.lblDiagnosis!.text = arr1[indexPath.row];
return cell;
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "medicineCell", for: indexPath) as! MedicineTableCell;
cell.lblMedicine!.text = arr1[indexPath.row];
return cell;
}
}

Multiple uitableview in a viewcontroller give an error

i have a problem with the following code that manage two uitableview in a viewcontroller. When insert the data in a modal controller in the 'directionTableView' give a follow error:
Thread 1: signal SIGABRT
'Could not cast value of type 'UITableViewCell' (0x1059e7560) to 'FoodTime.DirectionRecipeTableViewCell' (0x101388bf0). 2018-05-23 21:50:12.160281+0200 FoodTime[4577:360265] Could not cast value of type 'UITableViewCell' (0x1059e7560) to 'FoodTime.DirectionRecipeTableViewCell' (0x101388bf0).'
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = UITableViewCell()
if (tableView == self.ingredientTableView)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "newIngredientCell", for: indexPath) as! IngredientRecipeTableViewCell
let ingredientCell = ingredients[indexPath.row]
cell.textLabel?.text = ingredientCell.titleIngredientRecipe
cell.detailTextLabel?.text = ingredientCell.subtitleIngredientRecipe
}
else if (tableView == self.directionTableView)
{
//Thread 1: signal SIGABRT on next line
let cell = tableView.dequeueReusableCell(withIdentifier: "newDirectionCell", for: indexPath) as! DirectionRecipeTableViewCell
let directionCell = directions[indexPath.row]
cell.textLabel?.text = directionCell.directionSection
cell.detailTextLabel?.text = directionCell.directionText
}
return cell
}
Away from the problem the first line
var cell = UITableViewCell()
is actually what is being returned cells inside the if statements are local variables
So try this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if (tableView == self.ingredientTableView)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "newIngredientCell", for: indexPath) as! IngredientRecipeTableViewCell
let ingredientCell = ingredients[indexPath.row]
cell.textLabel?.text = ingredientCell.titleIngredientRecipe
cell.detailTextLabel?.text = ingredientCell.subtitleIngredientRecipe
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "newDirectionCell", for: indexPath) as! DirectionRecipeTableViewCell //Thread 1: signal SIGABRT
let directionCell = directions[indexPath.row]
cell.textLabel?.text = directionCell.directionSection
cell.detailTextLabel?.text = directionCell.directionText
return cell
}
}
Also make sure that you register every tableview with the corresponding cell
error i so easy
you declare this
var cell = UITableViewCell()
then you create new one
let cell = tableView.dequeueReusableCell(withIdentifier: "newIngredientCell", for: indexPath) as! IngredientRecipeTableViewCell
just remove let
Also
In interface builder make sure that cell with newDirectionCell is DirectionRecipeTableViewCell and same for another cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = UITableViewCell()
if (tableView == self.ingredientTableView)
{
cell = tableView.dequeueReusableCell(withIdentifier: "newIngredientCell", for: indexPath) as! IngredientRecipeTableViewCell
let ingredientCell = ingredients[indexPath.row]
cell.textLabel?.text = ingredientCell.titleIngredientRecipe
cell.detailTextLabel?.text = ingredientCell.subtitleIngredientRecipe
}
else if (tableView == self.directionTableView)
{
//Thread 1: signal SIGABRT on next line
cell = tableView.dequeueReusableCell(withIdentifier: "newDirectionCell", for: indexPath) as! DirectionRecipeTableViewCell
let directionCell = directions[indexPath.row]
cell.textLabel?.text = directionCell.directionSection
cell.detailTextLabel?.text = directionCell.directionText
}
return cell
}

UITableView checkmarks duplicated

I keep getting checkmarks being marked in other sections of my table view when I click a row. Im not certain if I need to set my accessoryType. I tried mytableView.reloadData() however that didn't help either.
var selected = [String]()
var userList = [Users]()
#IBOutlet weak var myTableView: UITableView!
#IBAction func createGroup(_ sender: Any) {
for username in self.selected{
ref?.child("Group").childByAutoId().setValue(username)
print(username)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
myCell.selectionStyle = UITableViewCellSelectionStyle.none
myCell.nameLabel.text = userList[indexPath.row].name
return myCell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if myTableView.cellForRow(at: indexPath)?.accessoryType == UITableViewCellAccessoryType.checkmark{
myTableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.none
let currentUser = userList[indexPath.row]
selected = selected.filter { $0 != currentUser.name}
}
else{
myTableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.checkmark
let currentUser = userList[indexPath.row]
selected.append(currentUser.name!)
}
}
Your problem is not inside this method but in the one that "loads" the cells. (cell for row)
Since Table Views use reusable cells, more often than not they will be loading a cell that was already presented somewhere else.
Because of this, on the cell loading method you should "Reset the state" the loaded cell, this includes the accessory type, and any other properties you might have changed.
So just change this in your code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
myCell.selectionStyle = UITableViewCellSelectionStyle.none
myCell.nameLabel.text = userList[indexPath.row].name
// ADD THIS
if userList[indexPath.row].isSelected {
myCell.accessoryType = UITableViewCellAccessoryType.checkmark
} else {
myCell.accessoryType = UITableViewCellAccessoryType.none
}
return myCell
}
EDIT:
"userList[indexPath.row].isSelected" is a property that YOU have to create and manage. (So you must also modify it in the didSelectRowAt method.
The issue is you are not maintaining the selected User info properly, which will be used while you scroll the table and when the cell has to reload data.
As you have already created var selected = [String]() , I suggest using the same.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
let dataFoundInselectedArr = selected.filter { $0 == userList[indexPath.row].name}
if(dataFoundInselectedArr.count > 0){
myCell.accessoryType = UITableViewCellSelectionStyle.checkmark
}else{
myCell.accessoryType = UITableViewCellSelectionStyle.none
}
myCell.nameLabel.text = userList[indexPath.row].name
return myCell
}
The table selection delegate method remains the same.

Using multiple UITableview in Single ViewController with Custom TableViewCell

How can I return a TableViewcell in cellForRowAtIndexPath delegate method , if I m using multiple UITableView in a Single ViewController, One TableView has Custom UITableViewCell and the other has default UITableViewCell . My problem is I m not able to cast the UITableViewCell to Custom TableViewCell type .
Code used as follows ,
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView == self.tvCity {
var cell:UITableViewCell?
cell = tableView.dequeueReusableCellWithIdentifier(cityCellIdentifier, forIndexPath: indexPath) as UITableViewCell
let row = indexPath.row
cell!.textLabel?.text = self.cityList[row].cityEn
}
if tableView == self.tvBranchByCity {
var cell:BranchNearMeTableViewCell?
cell = (tableView.dequeueReusableCellWithIdentifier(branchCellIdentifier, forIndexPath: indexPath) as! BranchNearMeTableViewCell)
let row = indexPath.row
cell.branchName = self.branchList[row].name// here cell.branchName is not accessible.
}
return cell!
}
Please advise . Thanks in advance.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView == self.tvCity {
var cell:UITableViewCell?
cell = tableView.dequeueReusableCellWithIdentifier(cityCellIdentifier, forIndexPath: indexPath) as UITableViewCell
let row = indexPath.row
cell!.textLabel?.text = self.cityList[row].cityEn
return cell!
}
if tableView == self.tvBranchByCity {
var cell:BranchNearMeTableViewCell?
cell = (tableView.dequeueReusableCellWithIdentifier(branchCellIdentifier, forIndexPath: indexPath) as! BranchNearMeTableViewCell)
let row = indexPath.row
cell.branchName = self.branchList[row].name as! String // here cell.branchName is not accessible.
return cell!
}
}
Differentiate tables with tag
see example :
if tableView.tag == 2000
{
identifier = "First Table"
let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)
return cell
}
else
{
identifier = "Second Table"
let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)
return cell
}

Resources