0

I have 2 UIButtons inside of a UICollectionViewCell, when the add button is pressed the price of the item is added to totalPrice and the button becomes hidden, and the Remove button becomes active. When the Remove button is pressed I want the price of the item to be removed from totalPrice and the Remove button to be hidden and the Add button to become active. It is for adding and removing items in a checkout flow. Right now the price subtracts and adds to totalPrice however after pressing Remove the button remains on screen. So pressing it multiple times will result in a negative number for totalPrice.

    class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, PostCellDelegate {

    var totalPrice = Double()
    private var hiddenRows = Set<Int>()


@objc func addPressed(_ sender : UIButton){
    hiddenRows.insert(sender.tag)
    let item = itemsArr[sender.tag].price
    totalPrice += Double(item) ?? 0
    sender.isHidden = true
    collectionView?.reloadData()
    print(item)
    print(totalPrice)
}



@objc func buttonTapped(cell: PostCell) {
      cell.myButton.isHidden = false
  }


override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

      let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell

    let item = itemsArr[indexPath.row]
    cell.delegate = self
    cell.set(name: item.name, brand: item.brand, price: item.price)
    cell.myButton.tag = indexPath.row


    cell.removeButton.addTarget(self, action: #selector(buttonTapped(cell:)), for: .touchUpInside)
    print(item)
      return cell
  }


   protocol PostCellDelegate {
func buttonTapped(cell: PostCell)
  }

    class PostCell: UICollectionViewCell {
     var delegate: PostCellDelegate?

     let myButton: UIButton = {
       let button = UIButton(type: .system)
       button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
    button.setTitle("Add", for: .normal)
       button.backgroundColor =  .red
   //    button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) //here....
       return button
   }()

let removeButton: UIButton = {
          let button = UIButton(type: .system)
          button.titleLabel?.font = UIFont.systemFont(ofSize: 12)
    button.setTitle("Remove", for: .normal)
          return button
      }()

     override func prepareForReuse() {
    super.prepareForReuse()
    self.delegate = nil
}
func set(name: String, brand: String, price: String){
       nameLabel.text = name
       brandLabel.text = brand
       priceLabel.text = price

   }
 override init(frame: CGRect) {
   // self.delegate = nil
     super.init(frame: frame)
    self.contentView.addSubview(containerView)
    containerView.addSubview(nameLabel)
    containerView.addSubview(brandLabel)
    containerView.addSubview(priceLabel)
    containerView.addSubview(myButton)
    containerView.addSubview(removeButton)
    containerView.addSubview(finalLabel)
    setupCellConstraints()


    }

func someButtonTapped(sender: UIButton){
    self.delegate?.buttonTapped(cell: self)
}
soRazor
  • 137
  • 9
  • You want `hiddenRows.remove(sender.tag)` in `removePressed`? Also, reloading the entire collection view is unnecessary; just reload the item that you updated. Finally, don't call `addTarget` in `cellForItemAt` - As cells are reused you will add the action handlers multiple times. You should have the cell itself handle the button clicks and provide a delegate or closure to the cell to process the clicks. This can also eliminate the need for `tag`s. See https://stackoverflow.com/questions/28659845/swift-how-to-get-the-indexpath-row-when-a-button-in-a-cell-is-tapped/38941510#38941510 – Paulw11 Feb 05 '20 at 21:53
  • so if I use this approach with delegates, I would just call something like this? "func buttonTapped(cell: PostCell) { cell.myButton.isHidden = false } – soRazor Feb 05 '20 at 22:28
  • Yes, but you would also need to update your `Set` so that you knew what cells were hidden and which weren't – Paulw11 Feb 05 '20 at 22:51
  • every time I hit the button now it gives me error EXC_BAD_ACCESS , I have set the delegate just like in the example. Any idea why? – soRazor Feb 05 '20 at 22:59
  • You need to edit your question to show the updated code that you have implemented. – Paulw11 Feb 05 '20 at 23:00
  • im a bit confuse about the someButtonTapped(), it must be wrong ? – soRazor Feb 05 '20 at 23:06
  • You are still adding the target in `cellForItem` - you need to do that once in the cell initialiser – Paulw11 Feb 05 '20 at 23:12
  • inside of PostCell? – soRazor Feb 05 '20 at 23:15
  • Yes. Because the cell needs to handle the tap and you only want to add the handler once – Paulw11 Feb 05 '20 at 23:16
  • I'm having trouble adding the functionality to the button, in awakeFromNib function it won't recognize myButton or removeButton at all. – soRazor Feb 05 '20 at 23:23
  • You should be able to add it in the cell `init(frame:)` - `self.removeButton.addTarget(self, action:#selector(someButtonTapped), for: .touchUpInside)` – Paulw11 Feb 05 '20 at 23:47
  • I am having trouble getting the data from that cell since I cannot call let item = itemsArr[sender.tag].price in buttonTapped because there is no sender. Do you know how I can retrieve the price from that index path? – soRazor Feb 06 '20 at 00:45
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/207315/discussion-between-paulw11-and-sorazor). – Paulw11 Feb 06 '20 at 00:46

1 Answers1

0

Simply setting isHidden property of objects in a reusable cell is not a good idea. You also need to update your dataSource(Array) to match the cells on your table view. In this case, you may add isAdded property in your item model and update its value when you touch the buttons. And set in cellForRowAt,

cell.myButton.isHidden = !item.isAdded
cell.removeButton.isHidden = item.isAdded

In this way, when calling reloadData, the tableView will update base on your data source which is the array.

Extra: Model property suggestion

struct Item {
  let name: String
  let brand: String
  let price: Double
  var isAdded: Bool = false
}

Note: First time answering so sorry for not so clear explanation. xD

Sky Shadow
  • 324
  • 2
  • 10