I don’t know a great way to describe this, but basically when I click on a button in a table view made up of the same reusable cell to view products in a certain category it’s supposed to then load the page, but instead of loading the target action it loads two actions every time. So if I click Top Picks, it’ll load Recommended, then Top Picks. If I click Recommended, it loads New Releases, then Top Picks. It’s like each button is being triggered for two things at once. I’m not sure what’s going on.
The only things these buttons have (The Button see All) is an outlet that points to flashDealCell, so I’m confused.
Video of it
https://imgur.com/a/BqQD1sW
My view hierarchy for the reusable cell
My code to register the actions
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
//for category
let cell = tableView.dequeueReusableCell(withIdentifier: "SeeAllProductsCell", for: indexPath) as! SeeAllProductsCell
cell.selectionStyle = .none
return cell
} else if indexPath.section == 1 {
//for featured products
let cell = tableView.dequeueReusableCell(withIdentifier: "flashDealCell", for: indexPath) as! flashDealCell
collectionview_featured = cell.collectionView_flashDeal
// collectionview_others.tag = indexPath.section
collectionview_featured.delegate = self
collectionview_featured.dataSource = self
cell.label_header.text = "Top Picks"
cell.button_seeAll.addTarget(self, action: #selector(SeeAllFeaturedProduct), for: .touchUpInside)
cell.selectionStyle = .none
if featuredData.count == 0 {
cell.label_noProduct.isHidden = false
}else
{
cell.label_noProduct.isHidden = true
}
cell.view_separator.isHidden = true
return cell
} else if indexPath.section == 2 {
//for new arrivals
let cell = tableView.dequeueReusableCell(withIdentifier: "flashDealCell", for: indexPath) as! flashDealCell
collectionview_newArrival = cell.collectionView_flashDeal
// collectionview_others.tag = indexPath.section
collectionview_newArrival.delegate = self
collectionview_newArrival.dataSource = self
cell.label_header.text = "New Releases"
cell.button_seeAll.addTarget(self, action: #selector(SeeAllNewArrivalsProduct), for: .touchUpInside)
cell.selectionStyle = .none
if newArrivalsData.count == 0 {
cell.label_noProduct.isHidden = false
}else
{
cell.label_noProduct.isHidden = true
}
cell.view_separator.isHidden = false
return cell
}else{
//for flash deals
let cell = tableView.dequeueReusableCell(withIdentifier: "flashDealCell", for: indexPath) as! flashDealCell
collectionview_flashDeals = cell.collectionView_flashDeal
// collectionview_others.tag = indexPath.section
collectionview_flashDeals.delegate = self
collectionview_flashDeals.dataSource = self
cell.label_header.text = "Recommended"//"On Sale"
cell.button_seeAll.addTarget(self, action: #selector(SeeAllFlashDealsProduct), for: .touchUpInside)
cell.selectionStyle = .none
if flashDealsData.count == 0 {
cell.label_noProduct.isHidden = false
}else
{
cell.label_noProduct.isHidden = true
}
cell.view_separator.isHidden = false
return cell
}
}
My triggered actions
@objc func SeeAllFlashDealsProduct() {
let vc = UIStoryboard.init(name: "Product", bundle: nil).instantiateViewController(withIdentifier: "FlashDealsVC") as! FlashDealsVC
vc.headerText = "Recommended"//"On Sale"
print("Clicked on Recommended")
self.navigationController?.pushViewController(vc, animated: true)
}
@objc func SeeAllFeaturedProduct() {
//--
let vc = UIStoryboard.init(name: "Product", bundle: nil).instantiateViewController(withIdentifier: "FlashDealsVC") as! FlashDealsVC
vc.headerText = "Top Picks"
print("Clicked on Top Picks")
self.navigationController?.pushViewController(vc, animated: true)
}
@objc func SeeAllNewArrivalsProduct() {
let vc = UIStoryboard.init(name: "Product", bundle: nil).instantiateViewController(withIdentifier: "FlashDealsVC") as! FlashDealsVC
vc.headerText = "New Releases"
print("Clicked on New Releases")
self.navigationController?.pushViewController(vc, animated: true)
}
2
Answers
I fixed it with a friends help
Adding the prepareForReuse helped make sure there was only one action per button, it's not the best way to do it but it works for right now
This falls under the subject of "what you shouldn’t do in
cellForRowAt
.Adding a target / action to a
UIButton
does not remove any existing target / actions.Look at this simple example – we’ll create one button, and
.addTarget
three times for three different funcs:If you run that, every time you tap the button you’ll see this in the debug console:
Because cells in a table view are reused, and you are dequeuing the same cell class for sections 1, 2 and 3, each time a cell is reused you are adding another target / action to the button.
This is one of the reasons you should design your cell class to handle the button tap internally, and use a
closure
(or protocol/delegate pattern) to allow the cell to inform the controller that the button was tapped.Your controller can then take the appropriate action, based on the
indexPath
of the cell.