1

UITableView 的两种复用 cell 方法的区别

 1 year ago
source link: https://hanleylee.com/articles/difference-between-two-tableview-dequeue-reusable-cell-methods/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

UITableView 的两种复用 cell 方法的区别

做过 iOS 开发的人都知道, iOS 的 UITableView 的 Cell 需要复用, 复用的时候有两种方法可以调用

那么他们到底有什么区别?

WaMa

之前没有深究过这个问题, 每次用的时候只要使用了 register(_:forCellReuseIdentifier:) 就能保证 App 的效果绝对不会有问题. 前两天心血来潮, 总觉得他们一定有什么不同, 尽管他们只差了一个参数而已

从 Apple 的 Documentation 中是很难看出区别的, 直到我看了 stackoverflow 上的答案. 区别有两点:

得到答案之后, 我们再回头看 Apple 的文档

  • dequeueReusableCell(withIdentifier:)

    … This method dequeues an existing cell if one is available or creates a new one using the class or nib file you previously registered. If no cell is available for reuse and you did not register a class or nib file, this method returns nil.

    If you registered a class for the specified identifier and a new cell must be created, this method initializes the cell by calling its init(style:reuseIdentifier:) method. For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell’s prepareForReuse() method instead.

  • dequeueReusableCell(withIdentifier:for:)

    Important: You must specify a cell with a matching identifier in your storyboard file. You may also register a class or nib file using the register(_:forCellReuseIdentifier:) method, but must do so before calling this method.

很明显, dequeueReusableCell(withIdentifier:for:) 的文档着重强调我们一定要在使用该方法前调用 register(_:forCellReuseIdentifier:), 为的就是防止 crash!

我们可以像下面这样使用 dequeueReusableCell(withIdentifier:), 在没有 register 的情况下手动调用指定 UITableViewCell 的初始化方法:

class TestTableViewVC: UITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

// MARK: - TableView Delegate & Data Source

extension TestTableViewVC {
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as? TestTableViewCell ?? .init(style: .default, reuseIdentifier: "TableViewCell")
        cell.setContent(with: indexPath.row.description)
        return cell
    }
}

但是如果使用 dequeueReusableCell(withIdentifier:for:) 的话, 必须与 register(_:forCellReuseIdentifier:) 配合使用

class TestTableViewVC: UITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(TestTableViewCell.self, forCellReuseIdentifier: "TableViewCell")
    }
}

// MARK: - TableView Delegate & Data Source

extension TestTableViewVC {
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TestTableViewCell
        cell.setContent(with: indexPath.row.description)
        return cell
    }
}

啰嗦了那么多, 其实总结下来很简单:

在看到这个 stackoverflow 时, 一个回答说:

经过我的验证, 这是完全错误的, 两种方法都是在 tableView(_:cellForRowAt:) 返回 cell 后才会被添加到 superview 上, 可能在之前的某个系统版本上出现过这样的 bug, 但是现在这个 bug 已经被解决了

本博客文章采用 CC 4.0 协议,转载需注明出处和作者。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK