更新 UITableView 单元格中的特定按钮图像

Posted

技术标签:

【中文标题】更新 UITableView 单元格中的特定按钮图像【英文标题】:Update Specific Button Image in UITableView Cell 【发布时间】:2021-04-22 05:57:12 【问题描述】:

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

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

【问题讨论】:

问题是由于单元格的可重用性,您需要以某种方式跟踪喜欢的行,然后相应地加载单元格。检查此表视图中的常见错误thomashanning.com/the-most-common-mistake-in-using-uitableview 【参考方案1】:

为您的按钮状态保留数据模型

试试下面的代码

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

【讨论】:

这就是答案,谢谢。你能向我解释它是什么以及它是如何一步一步发生的吗?只是为了让我能够接受更多的教育,而不仅仅是复制和粘贴 :) 如果你可以在你添加的代码中添加 cmets。 当用户退出应用程序并稍后返回时,本地保存单元格内存的最佳方法是什么。这样同样喜欢的单元格仍然喜欢吗?【参考方案2】:

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

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

创建细胞模型

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, UITableViewDelegate, 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
    


【讨论】:

我不能走这条路,因为我正在从 api 中提取数据,并且在加载数据之前永远不会知道我将拥有多少行。无论如何都要这样做? 在我的代码中,数组cellModels 只是一个示例。我已经提供了如何处理“喜欢”按钮的方法。您可能需要更新和扩展此代码。关于 API - 从 API 中提取数据后,您必须将其保存到 cellModels 并调用 tableView.reloadData()。其他一切都会像现在一样工作。 当我使用 UISearchBar 并拥有具有正确按钮状态的正确单元格时,此代码是否有效? 是的,正确的点赞按钮状态将按原样工作。

以上是关于更新 UITableView 单元格中的特定按钮图像的主要内容,如果未能解决你的问题,请参考以下文章

uitableview中的ios进度条

从已知按钮标签更改自定义单元格中的特定按钮标题

当 UITableView 单元格中的进度条不可见时,如何更新它们

UITableview 单元格中的按钮不会在快速点击时突出显示

仅在单元格中的特定 UIImage 上从 UITableView 推送详细信息视图?

从特定行获取 Picker 数据