Table View Scrolling Causes Unexpected Results [closed] - swift

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
My table view has a hidden image and when the user completes the level, this image becomes visible for the relevant cell. This process works with no issue.
Strangely, when i scroll the table view up and down, this hidden image begins randomly to appear on the other cells which belong to an incompleted level.
If I leave the view controller and get back, unexpected ones dissappear and if I start scrolling again, they come back.
Additionally, if all images are hidden (means there's no completed level), scrolling doesn't cause this issue. Here's the code piece I use:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell") as! CustomTableViewCell
cell.cellView.layer.cornerRadius = cell.cellView.frame.height / 2
cell.levelLabel.text = levels[indexPath.row]
cell.lockingImage.image = UIImage(named: lockState[indexPath.row])
if completeState[indexPath.row] == "true" {
cell.completedImage.isHidden = false
}
return cell
}
Any idea?

Let's say you are doing:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
cell.myImageView.isHidden = true
return cell
}
What you should do is:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
if levels[indexPath.row].isLevelCompleted {
cell.myImageView.isHidden = false
} else {
cell.myImageView.isHidden = true
}
return cell
}
This is because you are dequeuing the cell. Which means UITableView will use old cells. If you change the cell actively, you are good to go.

You probably did not reset the image visibility to false in the prepareForReuse function of the cells, which leads to the image to be visible whenever the cell in question is reused.

Related

Crash: Attempted to dequeue multiple cells for the same index path, which is not allowed

I have tableview datasource func to build a cell from a factory method function.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return OWTableViewOrganizer.instance.configureCell(at: indexPath)!
}
The factory method is here:
func configureCell(at indexPath: IndexPath) -> UITableViewCell? {
var cell = UITableViewCell()
switch indexPath.section {
case thisWorkoutSections.barbel.sectionNumber():
cell = barebellCell(indexPath: indexPath)
break
case thisWorkoutSections.lastWorkout.sectionNumber():
cell = lastWorkoutCell(indexPath: indexPath)
break
case thisWorkoutSections.personalRecord.sectionNumber():
cell = personalRecordCell(indexPath: indexPath)
break
case thisWorkoutSections.notes.sectionNumber():
break
default:
break
}
return cell
}
I have this code to build the cell:
func lastWorkoutCell(indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: WorkoutSetTableViewCell.cellIdentifier(), for: indexPath) as! WorkoutSetTableViewCell
if OWTableViewOrganizer.instance.lastWorkoutExerciseSets.count > 0 {
if indexPath.row < OWTableViewOrganizer.instance.lastWorkoutExerciseSets.count {
let logExerciseSet = OWTableViewOrganizer.instance.lastWorkoutExerciseSets[indexPath.row]
let setNumber = indexPath.row + 1
if let weight = logExerciseSet.weight?.doubleValue, let reps = logExerciseSet.reps?.intValue {
cell.setupCellWithData(setNumber: setNumber, weight: weight, reps: reps)
}
} else {
cell.setupCellWithData(setNumber: -1, weight: 0, reps: 0)
}
} else {
cell.setupCellWithData(setNumber: -1, weight: 0, reps: 0)
}
return cell
}
But time to time this line crashes for me:
let cell = tableView.dequeueReusableCell(withIdentifier: WorkoutSetTableViewCell.cellIdentifier(), for: indexPath) as! WorkoutSetTableViewCell
With error:
Attempted to dequeue multiple cells for the same index path, which is not allowed. If you really need to dequeue more cells than the table view is requesting, use the -dequeueReusableCellWithIdentifier: method (without an index path)
I know code style and design is not ideal here, please skip this if you have comments.
I don't know where to look, I tried simply remove indexPath, but it looks does not help or bring even more issues:
let cell = tableView.dequeueReusableCell(withIdentifier: WorkoutSetTableViewCell.cellIdentifier()) as! WorkoutSetTableViewCell
I have one controller which presents another one at the top of it (like in Apple music) and I can swipe down to show bottom controller and swipe up to bring back top controller. I noticed in log that I have some presentation alert, not sure if this something I need to deal with to resolve the issue above but JFY info.
It seems like there are two table view trigger
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
and you only dequeue cell from 1 table view at
let cell = tableView.dequeueReusableCell(withIdentifier: WorkoutSetTableViewCell.cellIdentifier(), for: indexPath) as! WorkoutSetTableViewCell
You should try to pass tableView from
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
to
lastWorkoutCell(indexPath: IndexPath)
(which will become lastWorkoutCell(indexPath: IndexPath, tableView: UITableView)) and dequeue cell from tableView

TableView DidSelectRowAt will change background colors of other cells when I scroll

I have a TableView sometimes with enough cells allowing me to scroll through the table. At the same time I have set my didSelectRow to switch the background color of the cell. It seems that after selecting a few then scrolling down I find that more cells have been selected out of my control.
Here is my didSelectRowAt:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
packSizesTableView.cellForRow(at: indexPath)?.backgroundColor = UIColor.yellow
packList[indexPath.row].picked = true
pickedRows.append(indexPath)
Is there another way I should be selecting my table cells other than changing the color if the indexPath.row?
You can set cell color in cellForRowAt method as below:-
If packList[indexPath.row].picked {
yourCell.backgroundColor = UIColor.yellow
} else {
yourCell.backgroundColor = UIColor.red
}
Short answer: You are seeing the colors change on the wrong cells because cells get reused, and you need to set the color to the correct option when a cell is reused.
In the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) method you can check your packList array to see if the cell being dequeued needs to be selected or not and set the color there. This will handle cases where a new cell is being created as well as cells that are being reused.
if packList[indexPath.row].picked {
cell.backgroundColor = UIColor.selectedColor
} else {
cell.backgroundColor = UIColor.defaultColor
}
You have several options for handling selecting/deselecting, so here is one option. In tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) you can simply do:
packList[indexPath.row].picked = !packList[indexPath.row].picked
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.whateverYouWant)
You could also do just update your packList array (which is needed for cellForRow), and override the logic in your cell's func setSelected(_ selected: Bool, animated: Bool) method.

How to pin Cell on select in Tableview?

I would like the selected cell to be pinned to the Tableview top and stay there while scrolling like section header when .plain style is used but for cell.
I don't know how to approach the problem. I know that the code should be placed at didSelectRowAt.
If your tableView cells are static, and you want the selected cell to be displayed at the top, You should try:
optional func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedRow = tableView.cellForRow(at: indexPath) as! yourTableViewCell // this wont apply if your cells are static
let firstVisibleIndexPath = tableView.indexPathsForVisibleRows.first
tableView.moveRow(at: indexPath, to: firstVisibleIndexPath)
}

UITableViewController, cells are reloading incorrectly

On my app, once I tap to open all my cells they are all loaded on function by the tableView delegated method:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> MyClassName {...}
So every time the cells are loaded, this method is called.
By the first time it loads properly (all cells with black background)
When I go down with the screen and it loads some different cells it also follows my code correctly and loads the cells with white background.
The problem happens when I go back to the top.
Its like the number of white background cells are brought up, make the cells once black backgrounded to become white ones (and that's not what I wanted). I thought that once the cells are loaded in the screen, it should stay on device's memory.
I've put some prints on my code to check if they are incorrect but apparently they are working properly.
So I ask you, did any of you had this kind of problem or a similar one? Do you think this is an iOS bug?
EDITED, ADDED CODE:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> HistoricoSelectedCell {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "historicoCell", for: indexPath) as! HistoricoSelectedCell
let item = itemChosen[indexPath.row].item
let cell = self.configureCell(cell: cell2, indexPath: indexPath, item: item!)
return cell
}
It is a cell re-use issue.
You generate a new cell to use in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell by calling tableView.dequeueReusableCell(withIdentifier: <identifier string>, for: indexPath) I presume (you really need to post code with your question).
The cell that you get is reused, as in it is not instantiated from scratch but has already been created and used for other cells.
Your options are to:
1) Set all the necessary properties in the cellForRowAt indexPath method.
2) Create a fund configure(cell: UITableViewCell) function that sets the appropriate properties
3) Override prepareForReuse() in your UITableViewClass and restore your properties there.

Swift TableView Prevent Button color change on selection

I am having a hard time preventing color change on my background color for my IBAction button when I select a row in my tableView. The background of my button is set to green, but when I tap the table view cell, it becomes gray. I have tried to change the color through different functions (tableView didSelectRowAtIndexPath etc..). Any ideas on how I would prevent the background of the button to change?
Thanks in advance!
I have faced same problem with button backgroundColor in TableView and solved it this way.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
cell.button.backgroundColor = UIColor.greenColor()
}
If you want to disable color change on selection entirely, change the selectionStyle attribute in your custom cell:
selectionStyle = .none
I solved the problem with the code Nirav provided me above:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
cell.button.backgroundColor = UIColor.green
}
However, I noticed that the button background color continued to turn gray when I tapped the row, and held the 'tap' without releasing. I solved this by adding the following code as well:
func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
cell.exploreButton.backgroundColor = UIColor.green
}