I have a collectionView embedded in tableView cell. CollectionView has multiple items, which contains button. Collection View datasource and delegates are getting set in UITableViewCell. I have to perform some action based on that button selection for that, I need to know collectionView cell indexPath and tableView cell indexPath. But not able to figure out, how to achieve this. Tried using delegates, but don't know how to get collectionView reference in delegate method.
CollectionView Cell
protocol SelectedItemCellDelegate:class {
func deleteButtonDidTapped(_ cell: SelectedItemCell)
}
class SelectedItemCell: UICollectionViewCell {
class var identifier: String{
return String(describing: self)
}
class var nib: UINib{
return UINib(nibName: identifier, bundle: nil)
}
@IBOutlet weak var deleteButton: UIButton!
weak var delegate: SelectedItemCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
}
@IBAction func deleteAction(_ sender: Any) {
delegate?.deleteButtonDidTapped(self)
}
}
ViewController
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier:SelectedItemCell.identifier, for: indexPath) as! SelectedItemCell
cell.delegate = self
return cell
}
extension PrescriptionVC: SelectedItemCellDelegate
{
func deleteButtonDidTapped(_ cell: SelectedItemCell)
{
// Need tableview indexPath as well SelectedItemCell indexPath.
}
}
You need two delegates
protocol selectCollectionCellDelegate {
func selectCell(cell : UICollectionViewCell )
}
protocol selectTableCellDelegate {
func selectTableCell(cell : UITableViewCell , indexPath : IndexPath )
}
class YourCollectionViewCell : UICollectionViewCell {
var tvcDelegate : selectCollectionCellDelegate
@IBAction func deleteAction(_ sender: Any) {
tvcDelegate.selectCell(cell : self)
}
}
class YourTableViewCell : UITableViewCell , selectCollectionCellDelegate {
var vcDelegate : selectTableCellDelegate
func selectCell(cell : UICollectionViewCell ){
let indexPath : IndexPath = collectionView.indexPath(for: cell)!
delegate.selectTableCell(cell : self , indexPath : indexPath )
}
}
class YourviewController : UIViewController , selectTableCellDelegate{
func selectTableCell(cell : UITableViewCell , indexPath : IndexPath){
//indexPatn is IndexPath of collectionViewCell
let tableCellindexPath : IndexPath = tableView.indexPath(for: self)!
}
}
You are discouraged from using delegates and view math in Swift for this purpose. Use a simple callback closure
In the cell delete the code related to the protocol, declare the closure and call it when a button is pressed
class SelectedItemCell: UICollectionViewCell {
class var identifier: String{
return String(describing: self)
}
class var nib: UINib{
return UINib(nibName: identifier, bundle: nil)
}
@IBOutlet weak var deleteButton: UIButton!
var callback : (() -> Void)?
@IBAction func deleteAction(_ sender: Any) {
callback?()
}
}
In the controller set the closure and handle the callback, the index path is captured.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier:SelectedItemCell.identifier, for: indexPath) as! SelectedItemCell
cell.callback = {
print("button pressed", indexPath)
}
return cell
}
If items can be inserted, deleted or moved just capturing the indexPath
in cellForItemAt
doesn't work because the index path can change without calling cellForItemAt
. In this case you have to pass the cell in the closure and get the actual index path
var callback : ((UICollectionViewCell) -> Void)?
@IBAction func deleteAction(_ sender: Any) {
callback?(self)
}
and
cell.callback = { currentCell in
print("button pressed", collectionView.indexPath(for: currentCell)!
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With