Multiple Cells Selected on Scrolling [Reuse Cells Problem]

multiple cells selected on scrolling [reuse cells problem]

Maintain global variable in your controller

var mLastSelectedIndex = -1

Whenever cellForRowAtIndexPath method called check if current cell is selected or not .If this is selected One update its UI. I suggest you to change selected cell UI on cell class only. This way you can easily manage cell tapping and helps you writing clean code.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = attachProfilesCollectionView.dequeueReusableCell(withReuseIdentifier: "attachCells", for: indexPath) as? attachUsersCell
cell!.subscribedUserId = self.followedUsers[indexPath.row].userId
cell?.profileNameToAttah.text = self.followedUsers[indexPath.row].fullName
cell?.profileImageToAttch.loadImagesWithUrl(from: self.followedUsers[indexPath.row].ImagePath)
// here update selected index path
if mLastSelectedIndex == indexPath.row {
cell?.isSelected = true
}
else{
cell?.isSelected = false
}

return cell!
}

Update last selected index on cell tapped.

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

guard mLastSelectedIndex != IndexPath.row else{return}
let indexpath = IndexPath(row:mLastSelectedIndex, section: 0)
mColllectionView.cellForItem(at: indexpath)?.isSelected = !mColllectionView.cellForItem(at: indexpath)!.isSelected
mLastSelectedIndex = IndexPath.row
}

In your attachUsersCell.swift declare isSelected property of cell . The default value of this property is false, which indicates that the cell is not selected.

override var isSelected: Bool{
didSet{
if isSelected {

setSelectedUI()

}
else{

setUnSelectedUI()

}

}
}

func setSelectedUI(){
borderColor = UIColor.systemOrange
borderWidth = 2
cornerRadius = 8
clipsToBounds = true
}

func setUnSelectedUI(){
// reset to default ui
}

Hope this helps!

Multiple Cells selected on scrolling in collection view

Because the cells are being reused that's why it's making a problem. Better use like this:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

static NSString *cellIdentifier = @"AppliancesCell";

_appliancesViewCell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

collectionView.allowsMultipleSelection = YES;
if ([cell isSelected]) {
_appliancesViewCell.contentView.backgroundColor = [UIColor blackColor];
}else {
_appliancesViewCell.contentView.backgroundColor = [UIColor clearColor];
}
return cell;
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {

_appliancesViewCell = (CategoryListCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];

_appliancesViewCell.contentView.backgroundColor = [UIColor blackColor];

}

- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath {

return YES;
}

-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {

_appliancesViewCell = (CategoryListCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];

_appliancesViewCell.contentView.backgroundColor = [UIColor clearColor];
}

my table view reuse the selected cells when scroll -- in SWIFT

You currently store the selection state in the backgroundColor. That's not a good idea because for performance reasons cells will be reused by the tableView.

You should save the selected indexPaths in the data model. If you want to allow multiple selection you can store the selected indexPaths in a NSMutableSet.

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.selectionStyle = .None
configure(cell, forRowAtIndexPath: indexPath)
return cell
}

var selectedIndexPaths = NSMutableSet()

func configure(cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
cell.textLabel!.text = "Row \(indexPath.row)"
if selectedIndexPaths.containsObject(indexPath) {
// selected
cell.backgroundColor = UIColor.redColor()
}
else {
// not selected
cell.backgroundColor = UIColor.whiteColor()
}
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if selectedIndexPaths.containsObject(indexPath) {
// deselect
selectedIndexPaths.removeObject(indexPath)
}
else {
// select
selectedIndexPaths.addObject(indexPath)
}
let cell = tableView.cellForRowAtIndexPath(indexPath)!
configure(cell, forRowAtIndexPath: indexPath)
}

If you only need single selection you should save the selected indexPath in an NSIndexPath? instance variable

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.selectionStyle = .None
configure(cell, forRowAtIndexPath: indexPath)
return cell
}

var selectedIndexPath: NSIndexPath?

func configure(cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
cell.textLabel!.text = "Row \(indexPath.row)"
if selectedIndexPath == indexPath {
// selected
cell.backgroundColor = UIColor.redColor()
}
else {
// not selected
cell.backgroundColor = UIColor.whiteColor()
}
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if selectedIndexPath == indexPath {
// selected same cell -> deselect all
selectedIndexPath = nil
}
else {
// select different cell
let oldSelectedIndexPath = selectedIndexPath
selectedIndexPath = indexPath

// refresh old cell to clear old selection indicators
var previousSelectedCell: UITableViewCell?
if let previousSelectedIndexPath = oldSelectedIndexPath {
if let previousSelectedCell = tableView.cellForRowAtIndexPath(previousSelectedIndexPath) {
configure(previousSelectedCell, forRowAtIndexPath: previousSelectedIndexPath)
}
}
}
let selectedCell = tableView.cellForRowAtIndexPath(indexPath)!
configure(selectedCell, forRowAtIndexPath: indexPath)
}

Collection view cells duplicate when scrolling

The problem is that you don't specify what text to appear on each of your CollectionViewCell's textView. As long as you don't specify the same in cellForItemAt indexPath it is going to show the reused cell and its content, from dequeueReusableCell as you said.

For the solution to your specific problem you can do as below in the viewcontroller:

`var textViewContentArray = [Int: String]()` //Create a dictionary to hold the texts globally

In textViewDidEndEditing:

func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
textView.textColor = .gray
textView.text = "Chapter Title"
} else {
guard let cell = textView.superview?.superview as? CustomWriterPageCell else {
return
}
guard let indexPath = self.collectionView.indexPath(for: cell) else { return }
textViewContentArray[indexPath.row] = textView.text
}
}

In textViewDidBeginEditing:

func textViewDidBeginEditing(_ textView: UITextView) {
textView.textColor = .black
}

And in cellForItemAt indexPath:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomWriterPageCell", for: indexPath) as! CustomWriterPageCell
cell.textView.delegate = self
let text = textViewContentArray[indexPath.row]
if !text.isEmpty {
cell.textView.textColor = .black
cell.textView.text = text
} else {
cell.textView.textColor = .gray
cell.textView.text = "Chapter Title"
}
return cell
}

Note: Here I am assuming you have set the Controller which holds the collectionView as delegate for the textViewInCell, if the cell is the delegate you can update the textViewContentArray using protocol

Hope this adds to your understanding.

Scrolling in UICollectionView selects wrongs cells - Swift

You don't want to call cellForItemAtIndexPath and configure the cells in the didSelectItemAtIndexPath or didDeselectItemAtIndexPath delegate methods.

Also, you shouldn't be calling selectItemAtIndexPath and deselectItemAtIndexPath from within the cellForItemAtIndexPath method.

Instead, just keep track and toggle the state of the selected category in your select/deselect callbacks, and then don't do anything other the set up up the look of your cells in cellForItemAtIndexPath.

As the commenter pointed out, the cells are re-used, so stick to the simple way the delegate callbacks are designed to be used, and you should have much better luck.

If you need to refresh the look of the cells, do it by relying on cellForItemAtIndexPath being called while scrolling and using the reloadData and reloadItemsAtIndexPaths collection view methods if you need to force an update.

Prevent selected cell from being reused

You can maintain a selectedIndex variable.
In your cellForRow you check whether this call is for selectedCell. If yes, then do the customisation that is required for selected cell.

Also you might want to handle heightForRow, there also check whether the call is for selected cell.

You can maintain an indexPath for selected cell. If there are multiple sections.
No need to prevent it from getting reused.

Hope that helps.

UICollectionView showing wrong cells after scrolling - dequeue issue?

I've had similare issues. This is most likely because the reused cells do not redraw themselves. In your custom cell's content class (your PDF view), trigger redrawing if the frame is updated:

-(void)setFrame:(CGRect)frame {
[super setFrame:frame];
[self setNeedsDisplay]; // force drawRect:
}

This worked for me. In addition, if your cell size may change, set the autoresizing mask so that it fills space with

self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

during initialization.



Related Topics



Leave a reply



Submit