0

I'm having something like a shopping cart page, it shows the products the user selected in a collection view, with each cell having 2 button linked to the product that will increase/decrease the amount of product to a singleton cartManager.

So far everything is working, i used protocol to ensure i know which product in the collection view that i'm adding/subtracting the product from. With this code:

protocol CartProductButtonDelegate : class {
    func cartProductPlus(_ sender: CartProductCell)
    func cartProductMinus(_ sender: CartProductCell)
}

class CartProductCell: UICollectionViewCell{
    //labels and image product details etc.
    @IBOutlet weak var productMinusBtn: UIButton!
    @IBOutlet weak var productPlusBtn: UIButton!
    weak var delegate : CartProductButtonDelegate?

override func awakeFromNib() {
    super.awakeFromNib()

    let tapPlusGesture = UITapGestureRecognizer(target: self, action: #selector(productPlusBtnTapped(_:)))
    tapPlusGesture.numberOfTapsRequired = 1
    self.productPlusBtn.addGestureRecognizer(tapPlusGesture)

    let tapMinusGesture = UITapGestureRecognizer(target: self, action: #selector(productMinusBtnTapped(_:)))
    tapMinusGesture.numberOfTapsRequired = 1
    self.productMinusBtn.addGestureRecognizer(tapMinusGesture)
}

@objc func productMinusBtnTapped(_ sender: UITapGestureRecognizer) {
    delegate?.cartProductMinus(self)
}

@objc func productPlusBtnTapped(_ sender: UITapGestureRecognizer) {
    delegate?.cartProductPlus(self)
}

}

And in my UIViewController, i add the collectionview delegate,datasource, and the custom protocol and make all the cell's delegate to the viewcontroller in cellForItem. Everytime i add or subtract the product, i reload the collectionview in order to show the correct amount on the cell label.

func cartProductPlus(_ sender: CartProductCell) {
    guard let tappedIndexPath = self.cartCollectionView.indexPath(for: sender) else {
        debugPrint("GUARD BROKE GETTING INDEX PATH FOR PRODUCT PLUS TAPPED")
        return
    }
    let product = self.productList[tappedIndexPath.item]
    debugPrint("cart Product Plus on product name: \(product.name), index : \(tappedIndexPath.item)")
    if let maxBought = Int(product.maxBought ?? ""){
        if cartManager.numberOfProductsInCart(product: product) < maxBought{
            cartManager.addProduct(product: product)
        }
    }
    self.rearrangeArray()//this is to reload the collection view as well as update UI on cart and someother stuff
}

The problem arise when i tried to add long press gesture using existing logic, for those people who want to buy in bulk.

I've tried to implement this:

let longPressPlusGesture = UILongPressGestureRecognizer(target: self, action: #selector(productPlusLongPressed(_:)))
self.productPlusBtn.addGestureRecognizer(longPressPlusGesture)

@objc func productPlusLongPressed(_ sender: UILongPressGestureRecognizer){
    if sender.state == .began || sender.state == .changed{
        delegate?.cartProductPlus(self)
    }
}

However when i long pressed the button, the item its adding are mixed up, the debug message are showing the collection view cell's index i'm receiving is going up in ascending order 0,1,2,3 then repeat 0,1,2,3 (depending on how many products are there in the collection view cell)

So question, is there a way to fix this? should i not reload collection view when i'm long pressing, if so, how do i update the UI to inform the user. Is there other method to work around the problem or should i just give up the idea of long press and just allow the user to tap the amount and edit it?

Mango Lord
  • 83
  • 9
  • You definitely need to avoid reloading the collection view while the long press is active because the position of the cells are moving around and that is just confusing. – rmaddy Apr 24 '19 at 04:36
  • Hmm, i've tried avoiding reloading the collectionView and created seperate protocol function for longpress and those function went for just reloading the individual item with reloadItems(at:) instead. However, the long press will update once, after that the subsequent function call, i can't get the individual index path, the guard was broken. Any idea? – Mango Lord Apr 24 '19 at 05:25
  • You can't reload the cell containing the active long press until the long press is complete. – rmaddy Apr 24 '19 at 06:21
  • Then how will i update the UI of the amount on that cell? – Mango Lord Apr 24 '19 at 08:33

1 Answers1

0

Alright, found a work around. After i've implemented the long press delegate, i split the protocol to two extra functions, one for when long press began/still press and the other for when long press ends.

func cartProductLongPlusStarted(_ sender: CartProductCell)
func cartProductLongMinusStarted(_ sender: CartProductCell)
func cartProductLongPlusEnded(_ sender: CartProductCell)
func cartProductLongMinusEnded(_ sender: CartProductCell)

However i do not update the UI from the viewcontroller when long press is active, i just update them from the cell itself. The cell will just hardcode to update the UI and only when the long press is finished, the view controller will just update the UI again.

@objc func productPlusLongPressed(_ sender: UILongPressGestureRecognizer){
    if sender.state == .began || sender.state == .changed{
        delegate?.cartProductLongPlusStarted(self)
        if var amount = Int(self.productCountLabel.text ?? "0"){
            if self.maxAmount != nil{
                if amount < self.maxAmount!{
                    amount += 1
                }
            }else{
                amount += 1
            }
            self.productCountLabel.text = String(amount)
        }
    }else{
        delegate?.cartProductLongPlusEnded(self)
    }
}

The only minor issue is that the long press seems to update too fast, the value might be updating abit too fast for the user to react when to stop properly, any idea to slow the updating the long press function call a bit?

Mango Lord
  • 83
  • 9