Обновляется больше ячеек, чем количество ячеек в таблице? - PullRequest
0 голосов
/ 08 января 2020

Я в тупике из-за простой проблемы в моем приложении Swift iOS. Я пытаюсь показать список 100 cells в tableview, где каждый cell говорит "Hello World".

В пределах cellForRowAt: Я имитирую сетевой вызов, внедряя режим ожидания в течение 2 секунд на background thread. После того, как этот симулированный сетевой вызов завершен, я обновил table view cell, чтобы текст отображался жирным шрифтом.

У меня есть 2 проблемы:

1) Прокрутка выглядит немного порывистый. Как это исправить?

2) Когда я запускаю приложение, если я ДЕЙСТВИТЕЛЬНО быстро scroll вниз до конца списка, я вижу, что обновлено более 100 ячеек (т.е. посмотрите на вывести выписку в cellForRowAt). Но если я медленно прокручиваю сверху вниз, я вижу обновленную 100 cells. Почему это происходит?

Вот основной ViewController.swift файл:

import UIKit

final class ViewController: UIViewController {

    // MARK: - Variables
    private var cellsUpdated: Int = 0
    private let fontSize17: CGFloat = 17
    private let tableview: UITableView = {
        let tv = UITableView()
        tv.backgroundColor = UIColor.white
        tv.translatesAutoresizingMaskIntoConstraints = false
        return tv
    }()
    private var reuseIdentifier: String = "cell"
    private var sampleData = [TableData]()

    // MARK: - View Life Cycle Methods
    override func viewDidLoad() {
        super.viewDidLoad()
        populateDataSet()
        setupTableView()
    }

    // MARK: - Custom Methods
    private func populateDataSet(){

        (0..<100).forEach { (_) in
            sampleData.append(TableData(value: "Hello World", hasBeenViewed: false))
        }
    }

    private func setupTableView(){

        self.tableview.register(UITableViewCell.self, forCellReuseIdentifier: reuseIdentifier)
        self.tableview.delegate = self
        self.tableview.dataSource = self
        view.addSubview(tableview)
        NSLayoutConstraint.activate([
            tableview.topAnchor.constraint(equalTo: self.view.topAnchor),
            tableview.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
            tableview.rightAnchor.constraint(equalTo: self.view.rightAnchor),
            tableview.leftAnchor.constraint(equalTo: self.view.leftAnchor)
        ])
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate
{
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)
        cell.textLabel?.text = self.sampleData[indexPath.row].value
        cell.textLabel?.font = UIFont.systemFont(ofSize: fontSize17)
        if !self.sampleData[indexPath.row].hasBeenViewed {
            cell.textLabel?.font = UIFont.systemFont(ofSize: fontSize17)
            DispatchQueue.global(qos: .userInteractive).async {
                // Simulate the execution of a CPU time intensive task on the background thread
                sleep(2)
                self.sampleData[indexPath.row].hasBeenViewed = true
                DispatchQueue.main.async {
                    // Update the table cells on the main thread
                    cell.textLabel?.font = UIFont.boldSystemFont(ofSize: self.fontSize17)
                    self.cellsUpdated += 1
                    print("\(self.cellsUpdated) cells have been updated")
                }
            }
        } else {
            cell.textLabel?.font = UIFont.boldSystemFont(ofSize: fontSize17)
        }
        return cell
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return sampleData.count
    }
}

Этот файл ссылается на класс с именем TableData, который является простым классом:

final class TableData {
    // MARK: - Internal Instance Variables
    var value: String
    var hasBeenViewed: Bool

    // MARK: - Init Method
    init(value: String, hasBeenViewed: Bool){
        self.value = value
        self.hasBeenViewed = hasBeenViewed
    }
}

Любая помощь будет принята с благодарностью. Спасибо!

Ответы [ 2 ]

2 голосов
/ 08 января 2020

1 - прокрутка не является изменчивой, она работает корректно

2- Когда вы добавляете эту строку здесь

sleep(2)
self.sampleData[indexPath.row].hasBeenViewed = true

, в то время как она должна быть здесь

if !self.sampleData[indexPath.row].hasBeenViewed {
   self.sampleData[indexPath.row].hasBeenViewed = true

Поскольку вы откладываете назначение hasBeenViewed = true, когда вы прокручиваете назад и вперед для 1 элемента if, запускается его вызываемое число раз> 1, так что вы видите результат> 100 в отличие от медленной прокрутки

0 голосов
/ 08 января 2020

Вы не должны обрабатывать сетевые запросы в этом методе, это неправильный выбор дизайна.

Если вам нужно получить данные для каждой ячейки, я бы порекомендовал вам создать ViewModel для этой ячейки, создайте метод configure, который принимает модель представления, внутри класса Cell, в viewModel, добавьте fetch или любой интенсивный метод, который вы хотите сделать.

Тогда это будет выглядеть так:

extension ViewController: UITableViewDataSource, UITableViewDelegate
{
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)
        cell.configure(viewModel: sampleData[indexPath.row])
  return cell
}

Делая это, вы получаете намного лучшее разделение проблем, теперь вам просто нужно сосредоточиться на своей тяжелой интенсивной задаче в ViewModel, и не забывать вызывать prepareForReuse() в ячейке, чтобы выполнить очистку. .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...