Search code examples
iosswiftuicollectionviewuicollectionviewcell

How to update the button tag which is part of UICollectionViewCell after a cell is deleted in UICollectionView?


Here's a problem which I have been stuck at for quite some time now. Here's the code

let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
collectionViewLove?.performBatchUpdates({() -> Void in

self.collectionViewLove?.deleteItemsAtIndexPaths([indexPath])
self.wishlist?.results.removeAtIndex(indexPath.row)

self.collectionViewLove?.reloadData()}, completion: nil)}

I have a button inside each UICollectionViewCell which deletes it on clicking. The only way for me to retrieve the indexPath is through the button tag. I have initialized the button tag in

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell    

However every time I delete, the first time it deletes the corresponding cell whereas the next time it deletes the cell follwing the one I clicked. The reason is that my button tag is not getting updated when I call the function reloadData(). Ideally, when I call the reloadData() ,

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell 

should get called and update the button tag for each cell. But that is not happening. Solution anyone?

EDIT:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    collectionView.registerNib(UINib(nibName: "LoveListCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "Cell")
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! LoveListCollectionViewCell
    cell.imgView.hnk_setImageFromURL(NSURL(string: (wishlist?.results[indexPath.row].image)!)!, placeholder: UIImage(named: "preloader"))
    let item = self.wishlist?.results[indexPath.row]
    cell.layer.borderColor = UIColor.grayColor().CGColor
    cell.layer.borderWidth = 1
    cell.itemName.text = item?.title
    cell.itemName.numberOfLines = 1
    if(item?.price != nil){
        cell.price.text = "\u{20B9} " + (item?.price.stringByReplacingOccurrencesOfString("Rs.", withString: ""))!
    }
    cell.price.adjustsFontSizeToFitWidth = true
    cell.deleteButton.tag = indexPath.row
    cell.deleteButton.addTarget(self, action: "removeFromLoveList:", forControlEvents: .TouchUpInside)
    cell.buyButton.tag = indexPath.row
    cell.buyButton.backgroundColor = UIColor.blackColor()
    cell.buyButton.addTarget(self, action: "buyAction:", forControlEvents: .TouchUpInside)
    return cell
}

Solution

  • A couple of things:

    1. You're doing too much work in cellForItemAtIndexPath--you really want that to be as speedy as possible. For example, you only need to register the nib once for the collectionView--viewDidLoad() is a good place for that. Also, you should set initial state of the cell in the cell's prepareForReuse() method, and then only use cellForItemAtIndexPath to update with the custom state from the item.

    2. You shouldn't reload the data until the deletion is complete. Move reloadData into your completion block so the delete method is complete and the view has had time to update its indexes.

    3. However, it would be better if you didn't have to call reloadData in the first place. Your implementation ties the button's tag to an indexPath, but these mutate at different times. What about tying the button's tag to, say, the wishlist item ID. Then you can look up the appropriate indexPath based on the ID.

    Revised code would look something like this (untested and not syntax-checked):

    // In LoveListCollectionViewCell
    override func prepareForReuse() {
        // You could also set these in the cell's initializer if they're not going to change
        cell.layer.borderColor = UIColor.grayColor().CGColor
        cell.layer.borderWidth = 1
        cell.itemName.numberOfLines = 1
        cell.price.adjustsFontSizeToFitWidth = true
        cell.buyButton.backgroundColor = UIColor.blackColor()
    }
    
    // In your UICollectionView class
    
    // Cache placeholder image since it doesn't change
    private let placeholderImage = UIImage(named: "preloader")
    
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.registerNib(UINib(nibName: "LoveListCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "Cell")
    }
    
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! LoveListCollectionViewCell
        cell.imgView.hnk_setImageFromURL(NSURL(string: (wishlist?.results[indexPath.row].image)!)!, placeholder: placeholderImage)
    
        let item = self.wishlist?.results[indexPath.row]
        cell.itemName.text = item?.title
        if(item?.price != nil){
            cell.price.text = "\u{20B9} " + (item?.price.stringByReplacingOccurrencesOfString("Rs.", withString: ""))!
        }
        cell.deleteButton.tag = item?.id
        cell.deleteButton.addTarget(self, action: "removeFromLoveList:", forControlEvents: .TouchUpInside)
        cell.buyButton.tag = item?.id
        cell.buyButton.addTarget(self, action: "buyAction:", forControlEvents: .TouchUpInside)
        return cell
    }
    
    func removeFromLoveList(sender: AnyObject?) {
        let id = sender.tag
        let index = wishlist?.results.indexOf { $0.id == id }
        let indexPath = NSIndexPath(forRow: index, inSection: 0)
    
        collectionViewLove?.deleteItemsAtIndexPaths([indexPath])
        wishlist?.results.removeAtIndex(index)
    }