Пакетные обновления UITableView: `reloadRows` перезагружает неправильную ячейку при использовании вместе с` moveRow` - PullRequest
0 голосов
/ 19 марта 2020

У меня возникли реальные проблемы с пакетным обновлением / анимацией UITableViewCell.

Я не могу перезагрузить ячейку, когда перед ней перемещается другая ячейка.

Не уверен, что я делаю что-то не так или есть ли ошибка в git в UITableView.

Пример

Учитывая начальный набор данных ячейки, упорядоченный следующим образом. ..

anna [19]
bob [16]
chloe [13]

при обновлении данных ячейки до ...

bob [17]
chloe [13]
anna [19]

... и попытке анимировать изменение в пакете с помощью (псевдо-кода) ...

moveRow(at: 0, to: 2)   // to move "anna" from first to last position
reloadRows(at: [1])     // to reload "bob" since its number changed from 16 to 17

... перезагружена неправильная ячейка, и я получаю ...

chloe [13]
chloe [13]
anna [19]

Результирующая анимация

enter image description here

Пример кода

class TableViewController: UITableViewController {

    private var data: [String] = [
        "anna [19]",
        "bob [16]",
        "chloe [13]",
    ]

    override func viewDidAppear(_ animated: Bool) {

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {

            print("---- PERFORMING BATCH UPDATES ----")

            self.data = [
                "bob [17]",  // this row has had its value updated
                "chloe [13]",
                "anna [19]", // this row has moved from its previous position of index 0
            ]

            self.tableView.performBatchUpdates({

                // Reload "bob" row
                let reloadIndexPath = IndexPath(row: 1, section: 0)
                self.tableView.reloadRows(at: [reloadIndexPath], with: .automatic)

                // Move "anna" row from index 0 to index 2
                let fromIndexPath = IndexPath(row: 0, section: 0)
                let toIndexPath = IndexPath(row: 2, section: 0)
                self.tableView.moveRow(at: fromIndexPath, to: toIndexPath)

            })
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let value = data[indexPath.row]
        cell.textLabel?.text = value
        print("DRAWING cell at indexPath: \(indexPath), value: \(value)")
        return cell
    }
}

Результирующий вывод на консоль

DRAWING cell at indexPath: [0, 0], value: anna [19]
DRAWING cell at indexPath: [0, 1], value: bob [16]
DRAWING cell at indexPath: [0, 2], value: chloe [13]
---- PERFORMING BATCH UPDATES ----
DRAWING cell at indexPath: [0, 1], value: chloe [13]

Чем этот вопрос не является

  • Я знаю, что Существуют ограничения для пакетных обновлений и невозможность перемещения и перезагрузки одной и той же ячейки 1039 * в одной партии без получения attempt to perform a delete and a move from the same index path cra sh (ссылки здесь & здесь )

  • ОДНАКО это НЕ то, что здесь происходит. Эта проблема перемещает одну ячейку и перезагружает другую (которая явно не перемещается).

  • Для вызова reloadRows я намеренно использую индекс 1, исходную позицию ячейки, а не 0, его окончательная позиция, потому что именно этого требует официальная документация Apple . Это также подтверждается тем фактом, что вызов reloadRows с индексом 0 вместо этого приводит к attempt to perform a delete and a move from the same index path cra sh.

Некоторые дополнительные замечания

  • Из журнала консоли видно, что tableView(_:,cellForRowAt:) вызывается как часть обновления, но перезагружается неправильная ячейка

  • Изменение порядка вызовов moveRow и reloadRows не имеет значения

  • Использование beingUpdates() / endUpdates в отличие от performBatchUpdates не имеет значения

Пожалуйста, помогите!


DifferenceKit

Ошибка, кажется, возникает и в DifferenceKit. Я обновил их демо-приложение, чтобы имитировать ту же ситуацию, и в результате получается анимация.

enter image description here

Код с проблемой доступен здесь ...

https://github.com/OliverPearmain/DifferenceKit/tree/reload-with-move-bug

И я поднял проблему с DifferenceKit здесь ...

https://github.com/ra1028/DifferenceKit/issues/98

1 Ответ

0 голосов
/ 19 марта 2020

Вы не можете выполнить batchUpdates с этим.

Причина в том, что когда вы делаете batchUpdates, UITableView будет делать все изменения одновременно. Это вызвало cra sh с ошибкой

attempt to perform a delete and a move from the same index path

Этот cra sh происходит потому, что когда UITableView перемещает ячейку анны вниз, она не будет перемещать ячейку боба вверх, пока все обновления не будут выполнено (следовательно, называется batchUpdates).

Когда UITableView пытается перезагрузить ячейку в IndexPath(item: 0, section: 0), она выходит из строя.

Решение:

Не выполняйте пакетное обновление. Просто переместите ячейку анны сначала вниз, а затем выполните перезагрузку ячейки Боба.

    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
        self.dataSource = [
            "bob [17]",
            "chloe [13]",
            "anna [19]"
        ]

        self.tableView.moveRow(at: IndexPath(item: 0, section: 0), to: IndexPath(item: 2, section: 0))
        self.tableView.reloadRows(at: [IndexPath(item: 0, section: 0)], with: .automatic)
    }

Для получения дополнительной информации:

примечание

beginUpdates устареет в следующем выпуске, замена будет performBatchUpdates

// Use -performBatchUpdates:completion: instead of these methods, which will be deprecated in a future release.
open func beginUpdates()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...