Kinuhatek
Kinuhatek

Reputation: 85

CollectionViewCell: Access to data of a cell through indexPath?

What I have: I have a CollectionViewCell as .xib + .swift files. Every cell gets their data from the database. Inside every cell I have a like button.

What I want: When I press the like button of a certain cell, I want to be able to read this data so I can change it and write the new data in the Database. So I want to change the like attribute of the dataset of a certain cell and save it in the DB

What I tried: I have the indexPath of the cell but how can I read the data of the cell?

    @IBAction func likeButton(_ sender: UIButton) {

        var superview = self.superview as! UICollectionView

        let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:superview)
        let indexPath = superview.indexPathForItem(at: buttonPosition)

        print(superview.cellForItem(at: indexPath!))
        
        // Change picture
        if sender.currentImage == UIImage(systemName: "heart.fill") {
            sender.setImage(UIImage(systemName: "heart"), for: .normal)
        } else {
            sender.setImage(UIImage(systemName: "heart.fill"), for: .normal)
        }
        
    }

UICollectionViewDataSource


    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.identifier,
                                                      for: indexPath) as! MyCollectionViewCell
        
        cell.backgroundColor = .white
        
        let newShoeCell = shoesArray?.randomElement()

        // Fill cells with data
        cell.imageView.image = UIImage(named: newShoeCell!.imgName)
        cell.shoeTitle.text = newShoeCell!.title
        cell.price.text = String(newShoeCell!.price)
        cell.ratingNumberLabel.text = String(newShoeCell!.meanRating)
        cell.floatRatingView.rating = newShoeCell!.meanRating
        
        if newShoeCell!.liked {
            cell.likeButtonOutlet.setImage(UIImage(systemName: "heart.fill"), for: .normal)
        } else {
            cell.likeButtonOutlet.setImage(UIImage(systemName: "heart"), for: .normal)
        }
        
        return cell
    }

Upvotes: 0

Views: 528

Answers (2)

Duncan C
Duncan C

Reputation: 131418

You need to change your thinking. It is not the data "of a cell" A cell is a view object. It displays information from your data model to the user, and collects input from the user.

You asked "...how can I read the data of the cell?" Short answer: Don't. You should be saving changes into your data model as you go, so once you have an index path, you should use it to index into your data model and get the data from there.

You need to figure out which IndexPath the tapped button belongs to, and fetch the data for that IndexPath from your data model.

If you look at my answer on this thread I show an extension to UITableView that lets you figure out which IndexPath contains a button. Almost the exact same appraoch should work for collection views. The idea is simple:

  • In the button action, get the coordinates of the button's frame.

  • Ask the owning table/collection view to convert those coordinates to an index path

  • Use that index path to fetch your data.

The only difference is that for collection views, the method you use to figure out which IndexPath the button maps to is indexPathForItem(at:) instead of indexPathForRow(at:)

Upvotes: 2

Alexandra
Alexandra

Reputation: 234

I suggest you add a property to your cell

class MyCell: UITableViewCell {
var tapAction: (() -> Void)?
...
@IBAction func likeButton(_ sender: UIButton) {
  tapAction?()
  ...
}

}

and set it in view controller

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  ...
  cell.tapAction = { [weak self] in
  // call to database or whatever here and then reload cell
  tableView.reloadRows(at: [indexPath], with: .automatic)
  ...
}

In general, cell should never care what its indexPath is, nor should it make calls to superview

Upvotes: 0

Related Questions