滚动浏览集合视图时重置按钮和标签

Posted

技术标签:

【中文标题】滚动浏览集合视图时重置按钮和标签【英文标题】:buttons and labels resetting when scrolling through collection view 【发布时间】:2018-07-30 19:04:21 【问题描述】:

我有一个集合视图,其中每个单元格都具有与用户交互的能力。每个单元格都有一个like 按钮和一个number of likes 标签。当按钮被按下时,按钮应该变成青色,并且标签(包含喜欢的数量)应该增加。此设置当前有效。但是,当我滚动浏览集合视图并向后滚动时,按钮会恢复为原始颜色(白色),并且标签会减少到原始值。我听说过一种表面上很有帮助的方法,叫做prepareForReuse(),但也许我没有正确使用它。这是我的代码:

这是包含所有单元格的数组

var objects = [LikableObject]()

这是这些对象的类定义

class LikableObject 

  var numOfLikes: Int?
  var isLikedByUser: Bool?

  init(numOfLikes: Int, isLikedByUser: Bool) 
    self.numOfLikes    = numOfLikes
    self.isLikedByUser = isLikedByUser
  

请注意,此对象中存在更多功能,但它们与此问题的目的无关。需要注意的一件重要事情是,每个单元格的数据都是使用 API 获取的。我正在使用 Alamofire 向 API 发出请求,该 API 将返回每个单元格的 numOfLikesisLikedByUser 属性的信息。

这是我使用集合视图的委托方法加载每个单元格的方式:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ObjectCell", for: indexPath) as! ObjectCell
    cell.configureCell(
      isLikedByUser: objects[indexPath.row].isLikedByUser!,
      numOfLikes:    objects[indexPath.row].numOfLikes!,
    )
    return cell
  

ObjectCell 类有这三个字段:

var isLikedByUser: Bool?
@IBOutlet weak var numOfLikes: UILabel!
@IBOutlet weak var likeBtn: UIButton!

而那个属于cell类的configureCell()方法就在这里:

public func configureCell(numOfLikes: Int, isLikedByUser: Bool) 
    self.isLikedByUser   = isLikedByUser
    self.numOfLikes.text = String(numOfLikes)
    if isLikedByUser 
      self.likeBtn.setFATitleColor(color: UIColor.cyan, forState: .normal)
     else 
      self.likeBtn.setFATitleColor(color: UIColor.white, forState: .normal)
    
  

最后,prepareForReuse() 方法在这里:

override func prepareForReuse() 
    if isLikedByUser! 
      self.likeBtn.setTitleColor(UIColor.cyan, for: .normal)
     else 
      self.likeBtn.setTitleColor(UIColor.white, for: .normal)
    
  

这不起作用。即使确实如此,我仍然不知道如何保持numOfLikes 标签不递减,或者是否应该这样做。我推测这个问题的很大一部分是我没有正确使用prepareForReuse() 方法......感谢您的帮助,谢谢。

【问题讨论】:

如果用户按下按钮,你是否正确更新了你的对象数组? 不,那是我的第一个计划。我意识到需要修改数组,因为这是集合视图从中获取数据的结构。但是,我意识到单元类与数组没有直接关系。这很不幸,因为处理单击“赞”按钮时发生的行为的方法在单元类中,因此如果不进行大量重新布线就无法影响数组.当然,这种重新布线很可能不是最佳实践...... 【参考方案1】:

prepareForReuse 不是修改单元格的地方,正如名称所述,您“仅”必须准备它以供重用。如果您更改了某些内容(例如视图的isHidden 属性),您只需将它们更改回初始状态。

你应该做什么,你可以在单元格内为isLikedByUser 实现didSet,并将你的修改应用到其中的likeBtn。 (这当然是快速解决方案)

长解决方案:这是一种反模式,您的单元格有一个名为isLikedByUser 的属性,TableViewCell 是一个视图,在所有架构中,视图应该尽可能地愚蠢地处理业务逻辑。正确的方法是在 ViewController 中实现的 configure-cell 方法中应用这些修改。

如果你觉得你会在不同的视图控制器中重用这个单元格,至少通过协议定义它并通过该协议与你的单元格对话。这样,您将拥有更可重用和可维护的代码。

【讨论】:

我什至没有想到这一点。我会尝试将该方法移至视图控制器 嗯,这只是 MVC 建议的一个想法。如果您正在设计一个单元格,从我的角度来看,可以有一个名为isLiked 但不是isLikedByUser 的字段。 isLiked 更像isHidden/isEnabled... 但isLikedByUser 表示其中的一些逻辑。【参考方案2】:

目前这一切都很好,唯一缺少的部分是单元格重用,您必须将点赞数的变化反映到您的模型数组中

class ObjectCell:UICollectionViewCell 

   var myObject:LikableObject!

cellForRowAt

cell.myObject = objects[indexPath.row]

现在在单元格自定义类中,您可以让对象反映对它的任何更改,确保您可以使用委托/回调或任何观察技术

【讨论】:

哦,从某种意义上说,这样做会将单元格与包含可爱对象的数组“链接”对吧? 因为 LikableObject 是类引用,所以是的 那么还有一个问题。我应该在prepareForReuse() 方法中进行这些更改吗?别人说我不需要。因为当我使用prepareForReuse() 方法时,这些变化就会反映出来。按钮保持正确的颜色,但numOfLikes 标签的变化比以前更加疯狂。我应该在prepareForReuse() 中实施更改吗?或者别的地方。 awakeFromNib() 不是放置它们的地方.. 我只是得到例外。有什么见解吗? 等等,我想通了。我想在configureCell() 方法中进行更改。非常感谢 是的,在 configureCell 中反映 myObject 值,prepareForSegue 只清除它不会反映任何内容【参考方案3】:

这里不需要prepareForResuse

您确实需要更新 tableview 底层的模型。验证这一点的一种方法是使用预先喜欢的模拟数据并查看该数据是否正确显示。

【讨论】:

以上是关于滚动浏览集合视图时重置按钮和标签的主要内容,如果未能解决你的问题,请参考以下文章

使用集合视图使视图控制器可滚动

滚动视图与集合视图。获取“可见图像”

集合视图按钮背景图像在滚动时重复

滚动时集合视图中的图像变高

自动大小表格视图单元格扩展高度中的嵌套水平集合视图使集合视图滚动重置当重新加载单元格以累积行

如何在按钮按下时在集合视图中滚动(水平)?