更新 UITableView 单元格中的特定按钮图像 创建细胞模型更新单元格类添加模型数组并使用

问题描述

我正在尝试向我的“喜欢”按钮添加一个操作。这样当用户点击单元格中的心形 UIButton 时,他们点击的单元格中的心形会更新为粉红色的心形,表明他们喜欢它。但相反,它喜欢他们点击的心脏和另一个他们没有互动的不同细胞中的随机心脏。我一整天都在做这件事,任何帮助将不胜感激。例如,如果我喜欢/点击我的心脏 UIButton,我点击的按钮图像会更新,但是当我向下滚动时,另一个随机心脏会从相同的第一个单元格按钮点击更新。

此外,当我滚动并且单元格离开视图并向上滚动时,图像返回到不喜欢的按钮,其他喜欢的按钮变得喜欢。

解决方法

为按钮状态保留数据模型

试试下面的代码

struct TableModel {
    var isLiked: Bool
}

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {

var dataSource: [TableModel] = []

@IBOutlet var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()
    overrideUserInterfaceStyle = .light
    dataSource = Array(repeating: TableModel(isLiked: false),count: 20)
        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.tableView.showsVerticalScrollIndicator = false
        self.tableView.reloadData()
}

func tableView(_ tableView: UITableView,didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath,animated: true)
    
}

func tableView(_ tableView: UITableView,numberOfRowsInSection section: Int) -> Int {
    dataSource.count
}

@objc func buttonSelected(_ sender: UIButton) {
    
    dataSource[sender.tag].isLiked = !dataSource[sender.tag].isLiked
    let indexPath = IndexPath(row: sender.tag,section: 0)
    tableView.reloadRows(at: [indexPath],with: .automatic)
}

func tableView(_ tableView: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath) as! TableViewCell
    
    cell.likeBtn.tag = indexPath.row
    cell.likeBtn.addTarget(self,action: #selector(buttonSelected(_:)),for: .touchUpInside)
    let isLiked = dataSource[indexPath.row].isLiked
    if isLiked {
        cell.likeBtn.setImage(UIImage(named: "liked"),for: UIControl.State.normal)
    } else {
        //set unlike image
    }
    return cell
}

}

,

目前,您有一个硬编码的行数,但无论如何您都需要一个带有数据模型的数据源。当您按下按钮时,您必须保存特定行按钮的状态。我建议您先创建一个模型。

这里我提供了一种简单(但足够灵活)的方法来做到这一点。我还没有调试它,但它应该可以工作,你可以看到这个想法。我希望这会有所帮助。

创建细胞模型

struct CellViewModel {
    let title: String
    var isLiked: Bool
    // Add other properties you need for the cell,image,etc.
}

更新单元格类

最好在单元格类中处理顶部动作。要在控制器上处理此操作,您可以像我一样关闭或委托。

// Create a delegate protocol
protocol TableViewCellDelegate: AnyObject {
    func didSelectLikeButton(isLiked: Bool,forCell cell: TableViewCell)
}

class TableViewCell: UITableViewCell {
    // add a delegate property
    weak var delegate: TableViewCellDelegate?
    
    @IBOutlet var titleTxt: UILabel!
    @IBOutlet var likeBtn: UIButton!
    //...
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // You can add target here or an action in the Storyboard/Xib
        likeBtn.addTarget(self,action: #selector(likeButtonSelected),for: .touchUpInside)
    }

    /// Method to update state of the cell
    func update(with model: CellViewModel) {
        titleTxt.text = model.title
        likeBtn.isSelected = model.isLiked
        // To use `isSelected` you need to set different images for normal state and for selected state
    }
        
    @objc private func likeButtonSelected(_ sender: UIButton) {
        sender.isSelected.toggle()
        delegate?.didSelectLikeButton(isLiked: sender.isSelected,forCell: self)
    }
}

添加模型数组并使用

这是使用模型的更新的 ViewController 类。

class ViewController: UIViewController,UITableViewDataSource {
    // Provide a list of all models (cells)
    private var cellModels: [CellViewModel] = [
        CellViewModel(title: "Title 1",isLiked: false),CellViewModel(title: "Title 2",isLiked: true),CellViewModel(title: "Title 3",isLiked: false)
    ]
    
    @IBOutlet var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        overrideUserInterfaceStyle = .light
        
        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.tableView.showsVerticalScrollIndicator = false
        self.tableView.reloadData()
    }
    
    func tableView(_ tableView: UITableView,didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath,animated: true)
    }
    
    func tableView(_ tableView: UITableView,numberOfRowsInSection section: Int) -> Int {
        // return count of cell models
        return cellModels.count
    }
    
    func tableView(_ tableView: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath) as! TableViewCell
        
        let model = cellModels[indexPath.row]

        // call a single method to update the cell UI
        cell.update(with: model)
         
        // and you need to set delegate in order to handle the like button selection
        cell.delegate = self
        
        return cell
    }
}

extension ViewController: TableViewCellDelegate {
    func didSelectLikeButton(isLiked: Bool,forCell cell: TableViewCell) {
        // get an indexPath of the cell which call this method
        guard let indexPath = tableView.indexPath(for: cell) else {
            return
        }

        // get the model by row
        var model = cellModels[indexPath.row]

        // save the updated state of the button into the cell model
        model.isLiked = isLiked
        
        // and set the model back to the array,since we use struct
        cellModels[indexPath.row] = model
    }
}