-1

I have a ProductVC.swift (ProductViewController) file and a ProductCell.swift. The ProductVC contains a UICollectinView and ProductCell is a specific UICollectionViewCell.

ProductCell.xib looks like this:

enter image description here

ProductVC contains an array with all the cell data (products) and populates the cells.

My goal: The user should have the possibility to like an product. He can do it by clicking the like button on the top right corner of every cell. Every cell shows a specific product which is specified by a productID.

My Problem: The like button action (IBAction func) is in the ProductCell. ProductCell doesn´t have the cell data. Cell data is stored in ProductVC in an array. So I don´t know how catch the product(productID) the user wants to like.

My Tries: With the code below I can get the indexPath of the cell where the user clicked the like button. But I can´t use this indexPath to get the product data because the data is stored in ProductVC. I could also store the data in ProductCell but it is not a clean way. Is it possible mb to give this indexPath to the ProductVC?

extension UICollectionView {
    func indexPathForView(_ view: UIView) -> IndexPath? {
        let center = view.center
        let viewCenter = self.convert(center, from: view.superview)
        let indexPath = self.indexPathForItem(at: viewCenter)
        return indexPath
    } 
}

let superview = self.superview as! UICollectionView
    
if let indexPath = superview.indexPathForView(button) {
    print(indexPath) // indexPath of the cell where the button was pressed
}

SOLVED Solution is a callback closure:

//UICollectionViewCell
var saveProductLike: ((_ index: Int) -> Void)?
@IBAction func likedButtonClicked(_ sender: UIButton) {
    print("Liked button clicked!")
    let productArrayIndex = calculateProductArrayIndex(for: sender)
    saveProductLike?(productArrayIndex!)
}

//UIViewController
cell.saveProductLike = { (index) -> Void in
    print(index)  
}
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Kinuhatek
  • 85
  • 2
  • 10
  • Do you really believe no one has ever wondered about this before? Really? I don't think so. So please search before asking. This is a well-known problem and is already well solved; there is no need to ask about it again. Good survey of techniques here: https://stackoverflow.com/questions/9274494/how-to-know-the-uitableview-row-number Nothing much has changed since then (except maybe the language). – matt Nov 09 '20 at 15:28
  • If you are iOS 14 only, there is one new addition to those techniques, namely, you can attach a UIAction directly to the button when you configure the cell in `cellForItem`. – matt Nov 09 '20 at 15:33

1 Answers1

0

There are several approaches to solve this but I'll talk about the most common one which is using delegation.

protocol ProductCellDelegate: AnyObject {
  func productCellDidPressLikeButton(_ cell: ProductCell)
} 

in ProductCell define a property weak var delegate: ProductCellDelegate? and in the target action of the like button inform your delegate

@objc private likeButtonPressed(_ sender: Any) {
  delegate?.productCellDidPressLikeButton(self)
}

In your view controller you could conform to the protocol and implement it like this:

func productCellDidPressLikeButton(_ cell: ProductCell) {
  guard let ip = collectionView.indexPath(for: cell) else { return }
  // process event, get product via index...
}

Then you need to set the view controller to be the delegate in collectionView(_:willDisplay:forItemAt:) or collectionView(_:cellForItemAt:): cell.delegate = self.

fruitcoder
  • 1,073
  • 8
  • 24
  • Thank you for you answer but your solution didn´t work for me. I also tryed a delegate solution but it didn´t work for me. Are you sure that your implementation is correct? I miss something like "productCell.delegate = self" in my ViewController. Anyway the problem is solved with a callback closure. – Kinuhatek Nov 09 '20 at 19:25
  • I edited my answer, of course you need to assign the protocol witness to the cell's `delegate` property – fruitcoder Nov 10 '20 at 11:24