简体   繁体   中英

Dequeue reusable cell with delay

I have a tableView where I insert 20 rows with delay using DispatchQueue method. First 10 rows appear fine. Problem starts with 11th one when Xcode begins to dequeue reusable rows. In simulator it looks like it starts to insert by 2 rows nearly at the same time (11th+12th, then 13th+14th).

I wonder why is that. Do DispatchQueue and tableView.dequeueReusableCell methods conflict? And if yes how to organize things properly?

var numberOfCells = 0

//My TableView
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "TextCell")! as UITableViewCell
    return cell
}

//Function that inserts rows
func updateTableView(nextPassageID: Int) {
    for i in 0...numberOfCells - 1 {
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(i)) {
            self.numberOfCells += 1
            let indexPath = IndexPath(row: i, section: 0)
            self.tableView.insertRows(at: [indexPath], with: .fade)
            }
    }
}

Your code didn't work for me. May be you missed something to mention in your question. But with the information that I understood, I did some modification and now it runs (tested in iPhone X) as expected. Following is the working full source code.

import UIKit

class InsertCellViewController: UIViewController, UITableViewDataSource {

    var dataArray:Array<String> = []
    let reusableCellId = "AnimationCellId"
    var timer = Timer()
    var index = -1

    @IBOutlet weak var tableView: UITableView!

//    UIViewController lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: reusableCellId)
        tableView.separatorStyle = .none
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        updateTableView()
    }

//     MARK : UITableViewDataSource

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: reusableCellId)!
        cell.textLabel?.text = dataArray[indexPath.row]

        return cell
    }

//    Supportive methods

    func updateTableView() {
        timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(updateCounting), userInfo: nil, repeats: true)
    }

    @objc func updateCounting(){

        if index == 19 {
            timer.invalidate()
        }

        index += 1
        let indexPath = IndexPath(row: index, section: 0)

        self.tableView.beginUpdates()
        self.dataArray.append(String(index))
        self.tableView.insertRows(at: [indexPath], with: .fade)
        self.tableView.endUpdates()
    }
}

I think using a Timer is the better solution in your use case:

private var cellCount = 0

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    cell.textLabel?.text = "Cell \(indexPath.row)"
    return cell
}

func addCells(count: Int) {
    guard count > 0 else { return }

    var alreadyAdded = 0
    Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] t in
        guard let self = self else {
            t.invalidate()
            return
        }

        self.cellCount += 1

        let indexPath = IndexPath(row: self.cellCount - 1, section: 0)
        self.tableView.insertRows(at: [indexPath], with: .fade)

        alreadyAdded += 1
        if alreadyAdded == count {
            t.invalidate()
        }
    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    addCells(count: 20)
}

Try to place code inside DispatchQueue.main.asyncAfter in

self.tableView.beginUpdates()
self.tableView.endUpdates()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM