How to get selected IndexPath from a stepper located inside a collection view cell?

[亡魂溺海] 提交于 2019-12-13 19:55:01

问题


I have collection view that has stepper inside the collection view cell used to increase the number of product like the image below.

I need to know, when I click a stepper on a collection view cell. how do I know the indexPath.item of that collection view cell? so I can modify the data using the selected indexPath in the View controller?

so If I change the stepper in the second cell, I will always get indexPath.item = 1

I previously think that the indexPath will come from didSelectItemAt method below. but it seems the didSelectItemAt method will not be triggered when I tap the stepper inside the collection view cell.

  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    }

so I think I can get indexPath from cellForRowAt IndexPath and using protocol delegate pattern. here is the method I made, and I got the wrong indexPath

So If I change the stepper in the second cell, I will NOT always get indexPath.item = 1 , It can be 2,3,0 etc.

here is the view controller code:

class WishListVC: UIViewController, ListProductCellDelegate {

    var products = [Product]()
    var selectedProduct : Product?

     // method from ListProductCellDelegate
    func stepperButtonDidTapped(at selectedIndexPath: IndexPath, stepperValue: Int) {

        // get selectedIndexPath
        // perform some action based on selectedIndexPath

    }
}

extension WishListVC : UICollectionViewDataSource, UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return products.count
    }

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

        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WishListStoryboardData.CollectionViewIdentifiers.productSliderCell.rawValue, for: indexPath) as? ListProductCell else { return UICollectionViewCell()}


        cell.productData = products[indexPath.item]
        cell.indexPath = indexPath // I send the indexPath to the cell.
        cell.delegate = self

        return cell
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        selectedProduct = products[indexPath.item]
        performSegue(withIdentifier: WishListStoryboardData.SegueIdentifiers.toProductVC.rawValue, sender: nil)
    }


}

and here is the code in the collection view cell:

protocol ListProductCellDelegate {
    func stepperButtonDidTapped( at selectedIndexPath: IndexPath, stepperValue: Int)
}

class ListProductCell: UICollectionViewCell {

    var indexPath: IndexPath?
    var delegate: ListProductCellDelegate?

    var productData : Product? {
        didSet {
            updateUI()
        }
    }

    @IBAction func stepperDidTapped(_ sender: GMStepper) {
        guard let indexPath = indexPath, let collectionView = collectionView else {return}
        self.delegate?.stepperButtonDidTapped(at: indexPath, stepperValue: Int(sender.value))
    }


    func updateUI() {
        // update the UI in cell.
    }
}

回答1:


You can try adding tag property to the stepper. So when you click on the stepper you can listen to its selector and determine which stepper was called. The tag value should be same as the item index.

Something like this

cell.stepper.tag = indexPath.row

Above code should go inside the cellForRowAt delegate function.

and then when user taps on the stepper call a function and check the tag value

Something like this

func stepperClicked(sender) {
    //check sender.tag value
    //Do something here with the tag value
}



回答2:


In Swift a callback closure is probably the most efficient solution. It's independent of index paths, tags, view hierarchy math and protocols.

In the cell add a callback property which passes the stepper instance. Delete both indexPath and delegate. The protocol is not needed at all.

In the IBAction call callback and pass the stepper instance

class ListProductCell: UICollectionViewCell {

    var callback: ((GMStepper) -> Void)?

    var productData : Product? {
        didSet {
            updateUI()
        }
    }

    @IBAction func stepperDidTapped(_ sender: GMStepper) {
        callback?(sender)
    }


    func updateUI() {
        // update the UI in cell.
    }
}

In the controller in cellForItemAt set the closure and handle the callback. The index path is captured in the method.

And don't guard cells. Force unwrap it. The code must not crash. If it does it reveals a design mistake. With the guard way the table view would display nothing and you don't know why.

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

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WishListStoryboardData.CollectionViewIdentifiers.productSliderCell.rawValue, for: indexPath) as! ListProductCell
    cell.productData = products[indexPath.item]
    cell.callback = { stepper in 
       // handle the callback
    }
    return cell
}



回答3:


You have the stepper because it is the sender in your action method. The stepper has a superview which is the cell (you might have to walk up more than one superview to reach the cell). Once you have the cell you can call indexPath(for:) to get the index path. Done.

https://developer.apple.com/documentation/uikit/uicollectionview/1618094-indexpath



来源:https://stackoverflow.com/questions/53737450/how-to-get-selected-indexpath-from-a-stepper-located-inside-a-collection-view-ce

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!