Reputation: 8710
I have an app using UITableView
where an array data is first fetched from a third party api, then each item of the array data needs some local processing which takes a bit of time, then that processed data needs to be inserted into the UITableView
. How long each item of the array needs for processing will vary a lot. Once the table reaches the bottom, more data is fetched and the same process occurs again.
The reason I am doing the processing on each item of the array data and adding it one by one instead of processing all of it at once and inserting it all at once is because this allows me to show at least some of the data to the user right away instead of the user waiting for a few seconds before all the data appears.
I have simulated my scenario with the following simple code without any API:
import UIKit
import SnapKit
struct Record : Codable {
var title : String?
var detail : String?
func titleAttributedText() -> NSAttributedString? {
guard let t = title else {
return nil
}
return NSAttributedString(string: t, attributes: [.foregroundColor : UIColor.white, .font : UIFont.systemFont(ofSize: 20, weight: .bold)])
}
func detailAttributedText() -> NSAttributedString? {
guard let d = detail else {
return nil
}
return NSAttributedString(string: d, attributes: [.foregroundColor : UIColor.darkGray, .font : UIFont.systemFont(ofSize: 14, weight: .regular)])
}
}
class ViewController: UIViewController, UITableViewDataSource {
var tableView = UITableView()
var records = [Record]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
fetchRecords()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return records.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reuseIdentifier = "cell"
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) ?? UITableViewCell(style: .subtitle, reuseIdentifier: reuseIdentifier)
let record = records[indexPath.row]
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
cell.textLabel?.attributedText = record.titleAttributedText()
cell.detailTextLabel?.attributedText = record.detailAttributedText()
if indexPath.row > records.count - 5 && !fetching {
fetchRecords()
}
return cell
}
var fetching = false
let group = DispatchGroup()
let queue = DispatchQueue(label: "delayer", attributes: .concurrent)
func fetchRecords(){
fetching = true
print("fetchRecords")
DispatchQueue.global().async {
for _ in 0...30 {
let timeout: TimeInterval = Double(arc4random_uniform(10)) / 100 // 0 to 0.1 seconds delay
self.group.enter()
self.queue.asyncAfter(deadline: .now() + timeout) {
self.group.leave()
}
_ = self.group.wait(timeout: .distantFuture)
DispatchQueue.main.async {
self.tableView.performBatchUpdates {
self.records.append(Record(title: "New data \(self.records.count + 1)", detail: "Delay: \(timeout)"))
self.tableView.insertRows(at: [IndexPath(row: self.records.count-1, section: 0)], with: .automatic)
}
}
}
self.fetching = false
}
}
}
My problem is that when the self.tableView.performBatchUpdates
is occurring, the scrolling becomes choppy. When you scroll towards the end of the list and new data is being inserted, the scrolling is choppy and frame rate drops. The above code is able to demonstrate it. In my real app, it's a lot worse.
Here's a video recording:
I have used Instruments Time Profiler
and I see the following:
As you can see, the _performBatchUpdates
is taking up a lot of time.
I am not sure how to fix it. Any suggestions?
EDIT:
I have found a temporary workaround by debouncing the calls to performBatchUpdates
by about 500ms. So instead of constantly spamming the performBatchUpdates
, I add the new records to a temporary array, then debounce a function which inserts the new records such that consecutive performBatchUpdates
aren't called in less than 500ms. With this said, I would still like a better solution of any.
Upvotes: 2
Views: 79