【问题标题】:TableViewCells Array reorders itself after updating TableViewTableViewCells 数组在更新 TableView 后重新排序
【发布时间】:2020-02-05 15:22:35
【问题描述】:

你好我的程序员朋友们,

我遇到了 swift 的 UITableView 的问题,我只是找不到错误。

我对 iOS 的 TableViews 非常熟悉,我知道他们如何使用队列来尽可能提高性能。

我通常只是在我的 ViewController 中构建一个 UITableViewCells 数组,然后根据该数组更新 TableView。使用这种技术,我可以为我的所有 TableView 使用一个 DataSource 类,它非常干净并且总是可以正常工作。

到目前为止: 这是我的第一个具有许多相同类型单元格的 TableView,如果用户按下最后一个单元格,即“LoadMore”单元格,我会向其中添加新单元格。 但问题是,如果按下“LoadMore”单元格,一旦 TableView 更新,TableView 甚至 tableViewCells 数组都会重新排序,甚至包含无法正确呈现的双重条目。

我找不到原因。一切正常,所有数据和 tableViewCells 数组肯定是正确的,直到 TableViews reloadData() 被调用。我现在多次检查它们在更新前后包含的内容。

这是我的 ViewController 的(简化的)代码,顺便说一下,它只包含 TableView:

import UIKit

class UserDataTableViewController: UIViewController, UITableViewDelegate {

    @IBOutlet weak var tableView: UITableView!

    //Auto updating TableView
    var tableViewCells = [UITableViewCell]() {
        didSet {
            dataSource.update(with: tableViewCells)
            tableView.reloadData()
            tableView.beginUpdates()
            tableView.endUpdates()
        }
    }

    //DataSource for Table View
    var dataSource = NormalTableDataSource(cells: nil)

    override func viewDidLoad() {
        super.viewDidLoad()

        //TableView
        tableView.dataSource = dataSource
        tableView.delegate = self
        tableView.separatorColor = .darkText

        //get next data
        nextData()
    }

    func nextData() {
        if let data = self.data {
            //some code that finds the next data and then calls:
            addDataToTableView(data: nextData, morePages: true)
        }
    }

    func addDataToTableView(data: Array<Any>, morePages: Bool = false) {
        //create temp array out of old tableViewCells array
        var tempCells = tableViewCells

        //remove LoadMore cell if it exists
        if (tempCells.count > 0) {
            tempCells.removeLast()
        }

        //(Rating is a simple Struct)
        if let ratings = data as? [Rating] {
            //This loop runs aprox. 25 times
            for rating in ratings {
                let cell = tableView.dequeueReusableCell(withIdentifier: "UserRatedAlbumCell") as! UserRatedAlbumCell
                cell.update(rating: rating)
                tempCells.append(cell)
            }
        }

        //add LoadMore cell
        if (morePages) {
            let loadMoreCell = tableView.dequeueReusableCell(withIdentifier: "LoadMoreCell")!
            tempCells.append(loadMoreCell)
        }

        //update array, which automatically updates the tableView
        self.tableViewCells = tempCells
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return tableViewCells[indexPath.row].frame.size.height
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let cell = tableViewCells[indexPath.row]

        if (cell.reuseIdentifier == "LoadMoreCell") {
            self.nextData()
        }

        tableView.deselectRow(at: indexPath, animated: true)
    }
}

这里是“UserRatedAlbumCell”类中的(也有一点简化的)代码:

import UIKit

class UserRatedAlbumCell: UITableViewCell {

    @IBOutlet weak var albumCoverImage: UIImageView!
    @IBOutlet weak var albumNameLabel: UILabel!

    //data
    var album: Album?
    var rating: Rating?

    func update(rating: Rating) {
        self.rating = rating
        albumNameLabel.text = ""
        albumCoverImage.image = nil

        //---Some async task with completion block that returns "album":
        {
            if let album = album {
                DispatchQueue.main.async {
                    self.update(album: album, rating: rating)
                }
            }
        }
        //---
    }

    func update(album: Album) {
        self.album = album

        albumNameLabel.text = album.attributes?.name

        if let url = album.attributes?.artwork.url(forWidth: 150) {
            albumCoverImage.downloaded(with: url)
        }
    }
}

这是我的“NormalTableDataSource”,如前所述,我几乎所有的 TableView 都使用了它:

import UIKit

class NormalTableDataSource: NSObject, UITableViewDataSource {

    private var cells: [UITableViewCell]?

    init(cells: [UITableViewCell]?) {
        self.cells = cells
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return cells?.count ?? 0
    }

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

        if let cells = cells {
            return cells[indexPath.row]
        } else {
            return UITableViewCell()
        }
    }

    func update(with cells: [UITableViewCell]?) {
        self.cells = cells
    }
}

有人知道为什么会这样吗? 如果需要,我可以提供 TableView 发生情况的屏幕截图。但是我可以告诉你,一旦第一次按下 LoadMore,所有的单元格都会重新排序,并且有很多空白单元格,这可能是由于 tableViewCells 数组中的双重输入造成的。

感谢您的帮助!

【问题讨论】:

  • 似乎非常错误。通常,您会跟踪 数据 中的更改(如您所展示的 [Rating] 数组)。然后,在cellForRowAt 中,您将出列可重用单元格。我从未见过在其他地方创建单元格并尝试将它们保存在数组中的方法,
  • dequeueReusableCell 正在从您的表格视图中返回现有单元格,您将这些单元格添加回数组中,这就是您看到重复行的原因。我认为你需要回去看看如何正确地使单元格出队。
  • 这种方法充满了问题。 Apple 提供了一种用于部署 tableViews 的机制 - 正如@JamesP 所说,您应该非常强烈地考虑使用它。
  • 哦,好的,很高兴知道。我曾经只是想按照我的做法去做,并且在更简单的 tableViews 中效果很好,所以我从不认为它是错误的。
  • 我会再看一下文档。从来没有想过在另一个地方出队一个可重用的单元格然后数据源可能是一个问题。谢谢大家!

标签: ios arrays swift uitableview queue


【解决方案1】:

长话短说 dequeueReusableCell 旨在从 'tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)' 调用,这就是您遇到问题的原因。

一个更详细的故事是它返回表格视图本身认为不再使用的单元格,您将这些单元格的引用存储在某处(如您的 tableViewCells 属性)的事实并没有改变任何东西,表格视图仍然有在有意义的情况下重复使用这些单元的权利。所以每次你调用 nextData() 你都会得到重复的单元格。

【讨论】:

  • 哦,好吧,不知道。谢谢!
猜你喜欢
  • 1970-01-01
  • 2014-08-20
  • 2023-03-15
  • 2023-04-03
  • 2015-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-04
相关资源
最近更新 更多