更新 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 单元格中的进度条不可见时,如何更新它们
UITableview 单元格中的按钮不会在快速点击时突出显示