自定义 UITableViewCells 和正确使用 prepareForReuse 和 removeFromSuperview

Posted

技术标签:

【中文标题】自定义 UITableViewCells 和正确使用 prepareForReuse 和 removeFromSuperview【英文标题】:custom UITableViewCells and proper use of prepareForReuse and removeFromSuperview 【发布时间】:2015-06-23 06:31:51 【问题描述】:

我有一些自定义 UITableViewCell,当我滚动时,自定义标签和图像视图变得非常复杂。像这样:

当我阅读 prepareForReuse 的文档时,不清楚该方法应该做什么。它说:

出于性能原因,您应该只重置单元格的属性 与内容无关的内容,例如 alpha、编辑和 选择状态。表视图的委托 tableView:cellForRowAtIndexPath: 应该总是重置所有内容 重复使用一个单元格。

但是,在我的自定义单元格中,我必须删除所有标签和图像视图。我目前在我的自定义表格单元格中的 prepareForReuse 中有这个。

我有三个问题: 1.内容翻倍是可以预期的吗?还是我做错了什么? 2.如果正常,是否只有通过删除FromSuperview元素来处理这个问题?我尝试将它们设置为 nil ,但这并不完全有效。 3. prepareForReuse 是否是删除FromSuperview 的合适位置?如:

    import UIKit

    class EKMenuItemCell: UITableViewCell 

  var screenWidth:CGFloat = 0
  var screenHeight:CGFloat = 0
  let leftMIPadding:CGFloat = 40.0
  let rightMIPadding:CGFloat = 90.0
  let rightPricePadding:CGFloat = 60.0
  var mainItemWidth:CGFloat = 0


  var miHeaderLabel = UILabel()
  var miDetailLabel = UILabel()
  var miPriceLabel = UILabel()

  func updateCell(menuItem: EKMenuItem)
    var bounds = UIScreen.mainScreen().bounds
    self.screenWidth = bounds.size.width
    self.screenHeight = bounds.size.height

    self.mainItemWidth = self.screenWidth - (self.leftMIPadding + self.rightMIPadding)
    self.renderMenuItemHeader(menuItem)
    self.renderMenuItemDetail(menuItem)
    //self.renderMenuItemPrice(menuItem)
  

  func renderMenuItemHeader(menuItem: EKMenuItem)
    miHeaderLabel=UILabel(frame: CGRectMake(leftMIPadding,0, self.mainItemWidth, CGFloat.max))
    miHeaderLabel.text=menuItem.header
    miHeaderLabel.numberOfLines=0
    miHeaderLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
    miHeaderLabel.sizeToFit()
    miHeaderLabel.frame = CGRectMake(leftMIPadding, 10, self.mainItemWidth, miHeaderLabel.frame.size.height)
    miHeaderLabel.layer.borderWidth = 2.0
    miHeaderLabel.layer.borderColor = UIColor.greenColor().CGColor
    menuItem.cellHeight = miHeaderLabel.frame.size.height + 10
    self.contentView.addSubview(miHeaderLabel)
  


  func renderMenuItemDetail(menuItem: EKMenuItem)
    //println("here is my menuItem.detail")

    miDetailLabel=UILabel(frame: CGRectMake(leftMIPadding, 0, self.mainItemWidth, CGFloat.max))
    miDetailLabel.text=menuItem.detail
    miDetailLabel.numberOfLines=0
    miDetailLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
    miDetailLabel.sizeToFit()
    miDetailLabel.frame = CGRectMake(leftMIPadding, (miHeaderLabel.frame.size.height + 20), self.mainItemWidth, miDetailLabel.frame.size.height)
    miDetailLabel.layer.borderWidth = 2.0
    miDetailLabel.layer.borderColor = UIColor.greenColor().CGColor
    menuItem.cellHeight += miDetailLabel.frame.size.height + 10.0
    self.contentView.addSubview(miDetailLabel)

  


      override func prepareForReuse() 
        println("here i am in prepareForReuse")
        miDetailLabel.removeFromSuperview()
        miHeaderLabel.removeFromSuperview()
        super.prepareForReuse()
      

编辑 1

这是控制器中的 cellForRowAtIndexPath:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

  let tmpTableItem = self.tableItems[indexPath.row]
  if let menuHeader = tmpTableItem as? EKMenuHeader 
    let tableViewCell = tableView.dequeueReusableCellWithIdentifier("MenuHeaderCell", forIndexPath: indexPath) as! EKMenuHeaderCell
    tableViewCell.selectionStyle = UITableViewCellSelectionStyle.None
    tableViewCell.updateCell(menuHeader)
    return tableViewCell
   else if let menuItem = tmpTableItem as? EKMenuItem 
    let tableViewCell = tableView.dequeueReusableCellWithIdentifier("MenuItemCell", forIndexPath: indexPath) as! EKMenuItemCell
    tableViewCell.updateCell(menuItem)
    return tableViewCell
  
  let tableViewCell = tableView.dequeueReusableCellWithIdentifier("TableViewCell", forIndexPath: indexPath) as! UITableViewCell
  return tableViewCell

编辑 2

根据 Wain 的评论,当将字符串设置为“”时,我应用的边框完全不正常。看起来这很笨拙,从现代 iPhone 的子视图中删除性能很好。

【问题讨论】:

你能粘贴你的 cellForRowAtIndexPath: 方法吗 @BabulPrabhakar 完成了 你能发布 cellForRowAtIndexPath: 方法吗?你在哪里添加标签? 您是否检查过 prepareForReuse 方法是否在 cellForRowAtIndexPath 之前被调用? thx @ZeMoon ,是的 prepareForReuse 肯定被调用了。 【参考方案1】:

您不应该删除视图,因为那样会产生重新创建它们的成本。相反,您应该将文本设置为零,但即使每个单元格始终都有文本,您也不需要这样做,因为新文本将在单元格可见之前替换旧文本。大多数用例不需要您使用prepareForReuse

重复表明您一直在添加新视图而不是更新变量,因此每次尝试删除它们时实际上都没有发生任何事情。

【讨论】:

thx Wain,这就是我认为它应该工作的方式,但它对我来说从来没有这样工作过。同意重新创建子视图不好,但将它们设置为 nil 并没有取得任何效果。这是返回不同自定义单元格的 tableView,因此可能发生了一些不寻常的事情。 不要将视图设置为 nil,这只会破坏您的单元格。您永远不应该在每次使用自定义单元格时都添加新的子视图。 所以,我更新了编辑#2,当我将边框应用到视图的图层时,结果充其量是不可预测的。所有这些都是 UILabel 中的可变行号,我觉得会有不可预测的结果。 如何调整单元格的大小?您的标签高度计算没有考虑可用宽度,因此看起来没有任何匹配项(这就是您在屏幕上看到的)。 我将高度写入单元格的模型(项目或标题到名为 cellHeight 的属性)。我的同事说我需要从单元格中单独计算出动态高度(这显然是一个性能问题)。我认为这只是使用带有多行标签的 UITableViewCell 的一个缺点。

以上是关于自定义 UITableViewCells 和正确使用 prepareForReuse 和 removeFromSuperview的主要内容,如果未能解决你的问题,请参考以下文章

对一个 tableView 使用多个 UITableViewCells

如何使用 begin-endUpdates() 和多个自定义单元格展开/折叠 UITableViewCells?

回收时不会重新布局自定义 iOS UITableViewCells

SearchController 不搜索自定义 UITableViewCells

如何防止自定义 UITableViewCells 在取消选择时闪烁白色?

一个NIB中的几个自定义UITableViewCells - 如何在代码中引用?