Multiple Tableviews inside Collection-view - ios

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

Related

Dequeue multiple cells conforming to same protocol

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
}

cellForRow(at: ) returns nil

So I have this function.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! customCell
changeCellProperty(selectedIndexPath: indexPath)
return cell;
}
func changeCellProperty(selectedIndexPath: IndexPath){
print("indexpath = \(selectedIndexPath)") . // printing [0,0] and all values
let cell = self.tableView.cellForRow(at: selectedIndexPath) as! customCell
// got nil while unwrapping error in above statement.
cell.label.text = ""
// and change other properties of cell.
}
I am not able to understand the error.
When I am getting the indexpath, then why I am not able to point a particular cell and change properties accordingly.
You cannot access a cell that has not yet been added to the tableView. That is what you are trying to do here in changeCellProperty method. So, if your dequeue works, then all you would need to do is pass the dequeued cell to that method.
func changeCellProperty(cell: customCell){
cell.label.text = ""
// and change other properties of cell.
}
Your cellForRowAt method would look like this.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! customCell
changeCellProperty(cell: cell)
return cell
}
Note: class names should be UpperCamelCase. So your customCell should be named CustomCell.

How do I proceed on adding multiple prototype cell's

I'm trying to add two prototype cell on my UITableView. However, I don't know how I could verify to be able to "return" the correct cells for each prototype. Can you guys give me a hand?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if ??? {
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = "Oculos"
return cell
}else{
let cellAdicionar = itensTableView.dequeueReusableCell(withIdentifier: "cellIdAdc", for: indexPath) as! tableviewBotaoAdicionar
cellAdicionar.botaoAdicionar.text = "Adicionar"
return cellAdicionar
}
}
Storyboard Picture
You need to set your model to answer that inside cellForRowAt
var arr = ["first","second","first"]
//
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = arr[indexPath.row]
if item == "first" {
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = "Oculos"
return cell
} else {
let cellAdicionar = itensTableView.dequeueReusableCell(withIdentifier: "cellIdAdc", for: indexPath) as! tableviewBotaoAdicionar
cellAdicionar.botaoAdicionar.text = "Adicionar"
return cellAdicionar
}
}

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.

Select what cell class to use in didEndDisplaying

In my tableView I can toggle between two cell classes depending on layout. So now I wonder how I can choose what cell class to select in the didEndDisplaying function.
Should I choose cell by using dequeueReusableCell like in the function bellow?
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if isBigCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellBig") as! BigTableViewCell
cell.myImageView.kf.cancelDownloadTask()
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellSmall") as! SmallTableViewCell
cell.myImageView.kf.cancelDownloadTask()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if isBigCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellBig") as! BigTableViewCell
let data = ads[(indexPath as NSIndexPath).row]
cell.configureWithData(data)
//Dont show highlight
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellSmall") as! SmallTableViewCell
let data = ads[(indexPath as NSIndexPath).row]
cell.configureWithData(data)
//Dont show highlight
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
}
The cell is given to you in the didEndDisplaying method. You determine its real type based on the indexPath, just like you did in the cellForRowAt method.
Do not dequeue another cell in didEndDisplaying. Just cast the provided cell based on the indexPath.
Since it seems that you don't use indexPath to determine then cell type, then your code should be something like this:
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if isBigCell {
if let bigcell = cell as? BigTableViewCell {
bigcell.myImageView.kf.cancelDownloadTask()
}
} else {
if let smallcell = cell as? SmallTableViewCell {
smallcell.myImageView.kf.cancelDownloadTask()
}
}
}

Resources