Memory leaks when calls dequeueReusableCell(withIdentifier:for:) swift - ios

I have to show a couple of different cells. I called tableView(_:cellForRowAt:) for that, and in the method I use two different IDs for different classes of UITableViceCell
Here is a simple code:
class SimpleView: UITableViewController {
...
let cellIdMain = "JournalMainCellID"
let cellIdExtra = "JournalMainSceneAddNewID"
...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == journals.count {
guard let cellAdding = tableView.dequeueReusableCell(withIdentifier: cellIdExtra, for: indexPath) as? JournalMainSceneAddNew else {
fatalError("Cannot connect to the cell")
}
return cellAdding
}
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdMain, for: indexPath) as? JournalMainSceneCell else {
fatalError("Cannot connect to the cell")
}
cell.numberOfCountriesLabel.text = "\(journals[indexPath.row].numberOFCountries)"
return cell
}
}
When I tried to find memory leaks I found:
When I click on the details I found:
Why this happened? It looks pretty simple and straightforward.
Updated: pictures were updated.

you are writing if conditions which will work only in one case as indexpath.row will only be equal to count
even though it will not work because after going through if it will execute block of code after if
which means your if block is waste
and why are you using cell.delegate??

Update your code with the following code.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == journals.count {
let cellAdding: JournalMainSceneAddNew = {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdExtra) as? JournalMainSceneAddNew else {
return JournalMainSceneAddNew(style: UITableViewCellStyle.value1, reuseIdentifier: cellIdExtra)
}
return cell
}()
return cellAdding
}
let cell: JournalMainSceneCell = {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdMain) as? JournalMainSceneCell else {
return JournalMainSceneCell(style: UITableViewCellStyle.value1, reuseIdentifier: cellIdMain)
}
return cell
}()
cell.numberOfCountriesLabel.text = "\(journals[indexPath.row].numberOFCountries)"
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
}

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

switching between different classes for UITableViewCell

I have two data sources, and two different classes for custom cells in my table.
I want by pressing one button to switch between sources and classes and update my UITableView accordingly.
Unfortunately It works only one time I switch from one set to another. It doesn't return back.
Hope my code will help to explain what I mean:
var displayMode : Int = 1
#objc func tappedButton(_ sender: UIButton?) {
if displayMode == 1 {
displayMode = 2
myTable.reloadData()
} else {
displayMode = 1
myTable.reloadData()
}
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if displayMode == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId,
forIndexPath: indexPath) as! Class1
cell.taskTitle.text = source1.text
return cell
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId,
forIndexPath: indexPath) as! Class2
cell.taskTitle.text = source2.text
return cell
}
}
Should I delete table cells before changing mode?
You use the same cellID in
let cell = tableView.dequeueReusableCellWithIdentifier(cellId,
forIndexPath: indexPath) as! Class1
and
let cell = tableView.dequeueReusableCellWithIdentifier(cellId,
forIndexPath: indexPath) as! Class2
Should be two different cells for 2 different classes (2 different IDS)
1) You need to create 2 separate classes for cells:
class FirstCellClass: UITableViewCell {}
class SecondCellClass: UITableViewCell {}
2) Then register the cells(or add cells in Storyboard):
tableView.register(FirstCellClass.self, forCellReuseIdentifier: String(describing: FirstCellClass.self))
tableView.register(SecondCellClass.self, forCellReuseIdentifier: String(describing: SecondCellClass.self))
3) Check display mode and return specific cell cellForRowAtIndexPath and items count in numberOfRowsInSection:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch displayMode {
case .first:
return firstDataSource.count
case .second:
return secondDataSource.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch displayMode {
case .first:
let cell = tableView.dequeueReusableCell(
withIdentifier: String(describing: FirstCellClass.self),
for: indexPath
) as! FirstCellClass
configureFirstCell(cell)
return cell
case .second:
let cell = tableView.dequeueReusableCell(
withIdentifier: String(describing: SecondCellClass.self),
for: indexPath
) as! SecondCellClass
configureSecondCell(cell)
return cell
}
}

Cast from "UITableViewCell" to unrelated type "UICollectionView" always fails

Actually I am trying to add CollectionView in TableViewCell but I am not able to handel it as you can see in case of adding TableView in TableViewCell works fine but in case of Collection view it shows error.
See the screen shot for your reference.
Can you please suggest how to fix this error.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (dataArray[indexPath.row]["type"].string == "Traffic") {
tableView.registerClass(TrafficCollectionViewCell.self, forCellReuseIdentifier: "TrafficCollectionView")
let TrafficCell = tableView.dequeueReusableCellWithIdentifier("TrafficCollectionView", forIndexPath: indexPath) as! TrafficCollectionViewCell
print("im in Traffic Cell")
TrafficCell.TrafficArray = dataArray[indexPath.row]["detail"].arrayObject!
TrafficCell.TrafficCollectionView.reloadData()
TrafficCell.TrafficViewController = self
return TrafficCell
}
else if (dataArray[indexPath.row]["type"].string == "News") {
tableView.registerClass(NewsTableViewCell.self, forCellReuseIdentifier: "NewsTableViewCell")
let NewsCell = tableView.dequeueReusableCellWithIdentifier("NewsTableViewCell", forIndexPath: indexPath) as! NewsTableViewCell
print("Im in News Cell")
NewsCell.NewsArray = dataArray[indexPath.row]["detail"].arrayObject!
NewsCell.NewsTableView.reloadData()
NewsCell.NewsTableViewController = self
return NewsCell
}
else if (dataArray[indexPath.row]["type"].string == "Category") {
tableView.registerClass(CategoryTableViewCell.self, forCellReuseIdentifier: "CategoryTableViewCell")
let CategoryCell = tableView.dequeueReusableCellWithIdentifier("CategoryTableViewCell", forIndexPath: indexPath) as! CategoryTableViewCell
print("Im in Category Cell")
CategoryCell.CategoryArray = dataArray[indexPath.row]["detail"].arrayObject!
CategoryCell.CategoryTableView.reloadData()
CategoryCell.CategoryTableViewController = self
return CategoryCell
}

Resources