I have a UICollectionViewCell
, with a UIButton
. And I have two different actions. The first one, when the user presses the cell, it will segue to anoth
Here's an elegant solution that only requires a few lines of code:
Swift 4+ code
class MyCustomCell: UICollectionViewCell {
static let reuseIdentifier = "MyCustomCell"
@IBAction func onAddToCartPressed(_ sender: Any) {
addButtonTapAction?()
}
var addButtonTapAction : (()->())?
}
Next, implement the logic you want to execute inside the closure in your
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCustomCell.reuseIdentifier, for: indexPath) as? MyCustomCell else {
fatalError("Unexpected Index Path")
}
// Configure the cell
// ...
cell.addButtonTapAction = {
// implement your logic here, e.g. call preformSegue()
self.performSegue(withIdentifier: "your segue", sender: self)
}
return cell
}
You can use this approach also with table view controllers.
You can not call performSegue
from your UICollectionViewCell
subclass, because there is no interface declared on UICollectionViewCell
like that.
The reason why it is working didSelectItemAtIndexPath()
is because i suppose the delegate of your UICollectionView
is a UIViewController
subclass, what has the function called performSegueWithIdentifier:()`.
You need to notify your UIViewController
when the button was clicked in your UICollectionViewCell
, for what you have various possibilities, like KVO or using delegate.
Here is a little code sniplet, how to use KVO. This solution is great, as long as you do not care, in which cell was the button pressed.
import UIKit
class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var button: UIButton!
}
class CollectionViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
}
extension CollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: CollectionViewCell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
// Add your `UIViewController` subclass, `CollectionViewController`, as the target of the button
// Check out the documentation of addTarget(:) https://developer.apple.com/reference/uikit/uicontrol/1618259-addtarget
cell.button.addTarget(self, action: #selector(buttonTappedInCollectionViewCell), for: .touchUpInside)
return cell
}
func buttonTappedInCollectionViewCell(sender: UIButton) {
self.performSegue(withIdentifier: "toStoreFromMyDiscounts", sender: nil)
}
}
EDIT: If you care, in which cell the touch event has happend, use the delegate pattern.
import UIKit
protocol CollectionViewCellDelegate: class {
// Declare a delegate function holding a reference to `UICollectionViewCell` instance
func collectionViewCell(_ cell: UICollectionViewCell, buttonTapped: UIButton)
}
class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var button: UIButton!
// Add a delegate property to your UICollectionViewCell subclass
weak var delegate: CollectionViewCellDelegate?
@IBAction func buttonTapped(sender: UIButton) {
// Add the resposibility of detecting the button touch to the cell, and call the delegate when it is tapped adding `self` as the `UICollectionViewCell`
self.delegate?.collectionViewCell(self, buttonTapped: button)
}
}
class CollectionViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
}
extension CollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: CollectionViewCell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
// Asssign the delegate to the viewController
cell.delegate = self
return cell
}
}
// Make `CollectionViewController` confrom to the delegate
extension CollectionViewController: CollectionViewCellDelegate {
func collectionViewCell(_ cell: UICollectionViewCell, buttonTapped: UIButton) {
// You have the cell where the touch event happend, you can get the indexPath like the below
let indexPath = self.collectionView.indexPath(for: cell)
// Call `performSegue`
self.performSegue(withIdentifier: "toStoreFromMyDiscounts", sender: nil)
}
}
Another solution that also works like a charm:
extension YOURViewController : UICollectionViewDataSource
{
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YOURCell", for: indexPath) as! YOURCollectionViewCell
cell.butTapped = {
[weak self] (YOURCollectionViewCell) -> Void in
// do your actions when button tapped
}
}
return cell
}
class YOURCollectionViewCell: UICollectionViewCell
{
var butQRTapped: ((YOURCollectionViewCell) -> Void)?
@IBAction func deleteButtonTapped(_ sender: AnyObject) {
butTapped?(self)
}
}