TableView 单元格重用

TableView CellReuse

我在使用 tableview 中的自定义单元格文件时遇到问题。我设法使用下面显示的注释行完成了它,但是当它有 10 个以上的单元格时,性能真的很差。 使用 dequeueReusableCell 导致此错误:

'NSInternalInconsistencyException',原因:'unable to dequeue a cell with identifier DiveNewsShort - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

这很奇怪,因为我确实在 viewDidLoad() 中注册了笔尖。我希望你能帮助我,我对此很沮丧。

class ProfilTableView: UITableViewController {

  override func viewDidLoad() {

    super.viewDidLoad()

    tableView.register(UINib(nibName: "DiveNewsShort", bundle: nil), forCellReuseIdentifier: "DiveNewsShort")

    tableView.register(DiveNewsShort.self, forCellReuseIdentifier: "DiveNewsShort")
  }

public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

  // let cell = Bundle.main.loadNibNamed("DiveNewsShort", owner: self, options: nil)?.first as! DiveNewsShort
  // This one works as expected

    let cell = tableView.dequeueReusableCell(withIdentifier: "DiveNewsShort", for: indexPath) as! DiveNewsShort
  // This one does not

return cell }


更新:

我设法通过在 cellForRowAt 函数中添加 register 函数来消除错误,但我认为这实际上不是一种有效的方法。它应该在 vieDidLoad 中工作,不是吗?

public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    tableView.register(UINib(nibName: "DiveNewsShort", bundle: nil), forCellReuseIdentifier: "DiveNewsShort")
    let cell = tableView.dequeueReusableCell(withIdentifier: "DiveNewsShort", for: indexPath) as! DiveNewsShort

return cell }

你不需要这一行:

tableView.register(DiveNewsShort.self, forCellReuseIdentifier: "DiveNewsShort")

您已经在前一行注册了 nib 文件。

我已经建立了这个协议来帮助我完成这个过程

protocol CBNibInstanceableCellProtocol {
    static func getCellXib() -> UINib?

    static func getReuseIdentifier() ->String
}

并且在您的 class 中,您必须像此处那样实施这些方法

//example implementation
extension CBUsersAttendanceEmptyCell : CBNibInstanceableCellProtocol
{
    static func getCellXib() -> UINib?
    {
        if Bundle.main.path(forResource: "CBUsersAttendanceEmptyCell", ofType: "nib") != nil
        {
            return UINib(nibName: "CBUsersAttendanceEmptyCell", bundle: nil)
        }
        return nil
    }

    static func getReuseIdentifier() ->String
    {
        return "CBUsersAttendanceEmptyCell"
    }
}

然后在你的viewDidLoad中你必须做这样的事情

//example code
self.collectionView.register(CBUsersAttendanceAvatarCell.getCellXib(), forCellWithReuseIdentifier: CBUsersAttendanceAvatarCell.getReuseIdentifier())
self.collectionView.register(CBUsersAttendanceCountCell.getCellXib(), forCellWithReuseIdentifier: CBUsersAttendanceCountCell.getReuseIdentifier())
self.collectionView.register(CBUsersAttendanceEmptyCell.getCellXib(), forCellWithReuseIdentifier: CBUsersAttendanceEmptyCell.getReuseIdentifier())

在你的 cellForRow

if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CBUsersAttendanceCountCell.getReuseIdentifier(), for: indexPath) as? CBUsersAttendanceCountCell
        {
            return cell
        }

您必须在 xib 中为您的视图定义 class,这非常重要,请查看此图片

希望对您有所帮助

reuse/dequeuing可以通过三种方式注册单元格:

  1. 您正在以编程方式创建单元格,在这种情况下,您在 viewDidLoad 中注册了 class。

  2. 您正在使用 NIB,在这种情况下,您在 viewDidLoad.

  3. 中注册了 NIB
  4. 您正在使用故事板单元格原型,在这种情况下您无需注册任何内容。故事板会为您完成所有这些工作。

既然你使用的是笔尖,你应该去掉class的注册,只注册笔尖。您应该在 viewDidLoad 内完成此操作。 as well as in .

中概述了此过程

正在查看 your MCVE, your problem was a result of a more fundamental mistake, where you had a UIViewController trying to use another view controller, which was a UITableViewController, to manage the table. But UITableViewController has its own UITableView and won't use the one that you have an @IBOutlet for, so you were registering the NIB for a table view you weren't seeing. There were a ton of other issues here (e.g. if you really want a view controller within a view controller, you have to do view controller containment calls, etc.), but the simplest solution was to excise this separate UITableViewController from the project and when this was fixed, it works precisely as we described. See https://github.com/robertmryan/Divers 的 MCVE 工作版本。

您也没有连接单元格中其他两个控件(开关和滑块)的插座。因此,如果您更改了这两个控件中的任何一个然后滚动,这些单元格将被重用,并且您会看到更改后的 UIKit 控件是为其他单元格完成的,但随后又被重用了。要解决这个问题,您的自定义 UITableViewCell subclass 应该有 all 控件的出口,并且 cellForRowAt 必须为所有这些出口设置值。您还需要一些机制让单元格在开关和滑块发生变化时通知视图控制器并相应地更新模型,因此当稍后为该行调用 cellForRowAt 时,它会知道 [=22] 的状态=] 适当地设置控件。一个常见的解决方案是使用协议委托模式。请参阅上面的 GitHub 存储库,它也说明了这种模式。