Abrcd18
Abrcd18

Reputation: 185

How to implement favorite button in tableView cell?

I need to implement a save button. When a user taps on the button, it has to be filled, by default it's unfilled. But when I run my app some of the buttons in tableView cell are already filled like that

Here's code from TableViewCell

var isFavorite: Bool = false

private let addToFavorites: UIButton = {
    let button = UIButton(type: .custom)
    button.setImage(UIImage(systemName: "heart"), for: .normal)
    button.tintColor = UIColor.white.withAlphaComponent(0.85)
    button.contentVerticalAlignment = .fill
    button.contentHorizontalAlignment = .fill
    return button
}()

override func awakeFromNib() {
        super.awakeFromNib()
    
    setFavoriteButtonUI()
    addToFavorites.addTarget(self, action: #selector(markFavorite), for: .touchUpInside)
}

@objc func markFavorite() {
    setFavoriteButtonImage()
}

private func setFavoriteButtonImage() {
        isFavorite = !isFavorite
        let imgName = isFavorite ? "heart" : "heart.fill"
        let favoriteButtonImage = UIImage(systemName: imgName)
        self.addToFavorites.setImage(favoriteButtonImage, for: .normal)
    }

private func setFavoriteButtonUI() {

    addToFavorites.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(addToFavorites)
    
    addToFavorites.topAnchor.constraint(equalTo: filmImgView.topAnchor, constant: 40).isActive = true
    addToFavorites.trailingAnchor.constraint(equalTo: filmImgView.trailingAnchor, constant: -20).isActive = true
    addToFavorites.heightAnchor.constraint(equalToConstant: 30).isActive = true
    addToFavorites.widthAnchor.constraint(equalToConstant: 40).isActive = true
}

In cellForRowAt indexPath method I added

tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.top)

Upvotes: 0

Views: 499

Answers (1)

black_pearl
black_pearl

Reputation: 2699

It is a classical problem for iOS beginners.

TableViewCell has a mechanism of reusing object pool.

When some cell with filled heart slides off the screen, the cell enters the reusing object pool.

The new cell appeared on the screen, maybe is created, or pulled from the reusing object pool.


The simple solution is Mark & Config.

Just to maintain the cell status data out the scope of cell, we usually have the status data source on the controller.

@objc func markFavorite() changes the status data source, then table reload data.

in func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell , config the cell heart status.


in Controller:

var selected = [Int]()


func select(index idx: Int){
   selected.append(idx)
   table.reloadData()
}


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
    // ...
    cell.proxy = self
    cell.config(willShow: selected.contains(indexPath.row), idx: indexPath.row)

}

in Cell:

var proxy: XXXProxy? // learn sth proxy yourself

var idx: Int?

func config(willShow toShow: Bool, idx index: Int){
     idx = index
     btn.isHidden = !toShow
}

// add the rest logic yourself

Upvotes: 1

Related Questions