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?
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.
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*/)
}
}