Propagate a custom event from a UICollectionViewCe

2019-06-23 23:07发布

问题:

I have a custom UICollectionViewCell with a button inside it. When I tap the button, an event is fired inside that subclass. I want to then trigger an event on the UICollectionView itself, which I can handle in my view controller.

Pseudo-code:

class MyCell : UICollectionViewCell {
    @IBAction func myButton_touchUpInside(_ sender: UIButton) {
        // Do stuff, then propagate an event to the UICollectionView
        Event.fire("cellUpdated")
    }
}

class MyViewController : UIViewController {
    @IBAction func collectionView_cellUpdated(_ sender: UICollectionView) {
        // Update stuff in the view controller 
        // to reflect changes made in the collection view
    }
}

Ideally, the event I define would appear alongside the default action outlets in the Interface Builder, allowing me to then drag it into my view controller code to create the above collectionView_cellUpdated function, similar to how @IBInspectable works in exposing custom properties.

Is there any way to implement a pattern like this in native Swift 3? Or if not, any libraries that make it possible?

回答1:

I don't understand your question completely but from what I got, you can simply use a closure to pass the UIButton tap event back to the UIViewController.

Example:

1. Custom UICollectionViewCell

class MyCell: UICollectionViewCell
{
    var tapHandler: (()->())?

    @IBAction func myButton_touchUpInside(_ sender: UIButton)
    {
        tapHandler?()
    }
}

2. MyViewController

class MyViewController: UIViewController, UICollectionViewDataSource
{
    //YOUR CODE..

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
    {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
        cell.tapHandler = {
            //Here you can do your custom handling
        }
        return cell
    }
}

Let me know if you face any issues.



回答2:

Best thing to do is to make a custom protocol for your custom cell class

protocol CustomCellProtocolDelegate {
   func custom(cell: YourCellClass, hadButton: UIButton, pressedWithInfo : [String:Any]?)
}

Make this cell class have this protocol as a peculiar delegate, and to trigger this delegate:

class YourCellClass: UICollectionViewCell {

   var delegate : CustomCellProtocolDelegate?

   var indexPath : IndexPath? //Good practice here to have an indexPath parameter

   var yourButton = UIButton()

   init(frame: CGRect) {
      super.init(frame: frame)
      yourButton.addTarget(self, selector: #selector(triggerButton(sender:)))
   }
   func triggerButton(sender: UIButton) {
      if let d = self.delegate {
         d.custom(cell: self, hadButton: sender, pressedWithInfo : /*Add info if you want*/)
      }
   }

}

In your controller, you conform it to the delegate, and you apply the delegate to each cell in cellForItem: atIndexPath:

class YourControllerThatHasTheCollectionView : UIViewController, CustomCellProtocolDelegate {

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "identifier", for: indexPath) as! YourCellClass
        cell.delegate = self
        cell.indexPath = indexPath
        return cell
    }

   func custom(cell: YourCellClass, hadButton: UIButton, pressedWithInfo : [String:Any]?) {

        //Here you can process which button was selected, etc.. and apply your changes to your collectionview

   }

}

Best practice is to pass the cell's indexPath parameter in the delegate method inside of pressedWithInfo. It saves you the trouble of calculating which cell actually was pressed; hence why i usually add an indexPath element to each of my UICollectionViewCell subclasses. Better yet, include the index inside the protocol method:

    protocol CustomCellProtocolDelegate {
       func custom(cell: YourCellClass, hadButton: UIButton, pressedAt: IndexPath, withInfo : [String:Any]?)
    }

    func triggerButton(sender: UIButton) {
       if let d = self.delegate {
           d.custom(cell: self, hadButton: sender, pressedAt: indexPath!, withInfo : /*Add info if you want*/)
       }
    }