1

Making a download page in APP, and there are over 10 UIProgressView in the UITableView. The progress bars work fine, but once they are scrolled outside the screen and scrolled back, they update once, then they refuse to update more though they are still downloading.

Here is the code (clear some other code for a clean look):

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

    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! DownloadCell

    cell.dnProgress.setProgress(pg, animated: true)
    cell.dnProgress.isHidden = false
    return cell
}

 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    for v in (cell.subviews){
         if let p = v as? UIProgressView{
            self.downloadingProgress.append(p)
         }
    }
    goDownload(cloudData[indexPath.row)
}

 func updateProgress(_ theData:String, percent:Float){
        let i:Int = downloadingArray.index(of: theData)
        self.downloadingProgress[i].setProgress(percent, animated: true)
    }

I think it's something about cell reusing, anyone can help?

* Solution inspired by Aaron's idea *

Add one more array to store downloading cell indexPath

 func updateProgress(_ theData:String, percent:Float){
     let i:Int = downloadingArray.index(of: theData)
     let indexPath = dndingIndexPathArr[i]
     if let cell = tableView.cellForRow(at: indexPath) as? DownloadCell{
        cell.dnProgress.setProgress(percent, animated: true)
    }
    saveData.set(percent, forKey: "\(theData)Progress")
}
jdleung
  • 1,088
  • 2
  • 10
  • 26
  • What are the objects in the `downloadingProgress` array? `UIProgressView`s? – Aaron Jun 11 '18 at 14:27
  • @Aaron UIProgressView. They works fine without scrolling outside the screen. – jdleung Jun 11 '18 at 14:29
  • What does the function at `cellForRowAtIndexPath`, look like? Are you also cleaning up and preparing cells for reuse with `prepareForReuse`? – Aaron Jun 11 '18 at 14:31
  • I reposted function at cellForRowAtIndexPath, other code cleared – jdleung Jun 11 '18 at 14:47
  • Are you preparing each cell for reuse incase it is reused? i.e. in `prepareForReuse`? https://developer.apple.com/documentation/uikit/uitableviewcell/1623223-prepareforreuse – Aaron Jun 11 '18 at 14:49
  • I did not implement this function, try it later. – jdleung Jun 11 '18 at 14:53

1 Answers1

0

I created a sample app a few years ago that demonstrates this exact technique. It is not perfect:

https://github.com/chefnobody/Progress

My guess is that you're not correctly modeling the progress of each download. It is important to keep the modeling of that data (and any async requests) separate from the rendering of the table view cells because iOS will set up and tear down each cell as it scrolls off screen. You may also have some threading issues going on, as well.

I would brush up on the UITableViewCell life cycle events and ensure that you fully understand what's happening when a cell scrolls off screen.

Aaron
  • 7,055
  • 2
  • 38
  • 53
  • I guess is about cell reusing. After start downloading, I scroll those UIProgressView outside the screen, and then scroll back, the progress bar suddenly push forward for a distance. This shows that they keep downloading. Finally the progress bars just don't go but all files complete downloading. – jdleung Jun 11 '18 at 14:39
  • I bet that your progress indicators are not correctly tied to the HTTP requests for which they're presenting state. Here's how I modeled the requests: https://github.com/chefnobody/Progress/tree/master/Progress/Model – Aaron Jun 11 '18 at 14:44
  • I tried to print out the progress, it's going. And important is that the download can be completed correctly, problem is only the progress bar don't go after scroll outside the screen. :-( – jdleung Jun 11 '18 at 14:50
  • I'll take a try on your suggested link. – jdleung Jun 11 '18 at 14:50
  • Your array of `UIProgressView`s might be incorrect once the cells scroll off screen. Are you sure the progress views in that array are valid once a cell scrolls off screen? – Aaron Jun 11 '18 at 14:51
  • Remember, you're retaining an array of `UIProgressView` objects, whose parent cells are being cleaned up and manipulated by the cell reuse pool. I would strongly recommend against trying to manage the progress views that way. Rather I would model the progress of each download in a different object and then as cells appear on screen update the "state" of the cell with the progress as it changes. – Aaron Jun 11 '18 at 14:54
  • Yes, it's exact what I think, you describe it clearer. And I just don't how to do that. – jdleung Jun 11 '18 at 14:56
  • I also tried get cell(by tag) with `tableView.visibleCells` in function `updateProgress`, then update the cell's progress bar. Sometimes works sometimes doesn't. – jdleung Jun 11 '18 at 15:01
  • As I said before. You want to avoid this approach and model your downloads in a separate class, store them in an array and retrieve them by their `index`. The model should represent all the "state" that the Cell might need. For example here's how I model a `Download` in my project: https://github.com/chefnobody/Progress/blob/master/Progress/Model/Download.h – Aaron Jun 11 '18 at 16:33
  • Once you have a bunch of `Download` instances in an array, you can access them by their index and then set the various bits of UI based on those properties. – Aaron Jun 11 '18 at 16:33
  • I used to judge which progress bar by downloadingArray. According to your idea, now I create one more array to store downloading cell indexPath, then in function `updateProgress` update the progress bar by this `if let cell = tableView.cellForRow(at: indexPath) ` and `cell.dnProgress.setProgress(percent, animated: true)`. I tried for times, it works great! Thanks! – jdleung Jun 12 '18 at 02:40