2

I have UITableView with custom cells. I want to show progress indicator for multiple images upload.

I have tried reloadRowAtIndexPath method of UITableView but it not sufficient solution because cell is continuously blinks which looks weird.

Another one solution i found is to store reference of my progress indicator view placed in UITableViewCell in global variable and then modify it outside UITableView datasource methods, but in this solution i faced one problem which is i have to keep track of multiple progress indicator view objects of UITableViewCell which is difficult because UITableView datasource is two dimensional NSMutableArray(In short array inside array) so i don't have unique IndexPath.row because of multiple sections. So how can i manage objects of progress indicator view?

And also Is there any better solution to do this type of job?

Dhaval Dobariya
  • 171
  • 1
  • 12

5 Answers5

1

Ok, so here is what I used in one of my projects when I could not find anything else.

Swift 3

Make a sub class of NSObject (because a sub class of URLSession won't let you set configuration and other parameters as the only designated initializer there is init()) that includes the information of the cell that started the upload process as in IndexPath and also a URLSession object.

Use this sub class to create new URLSession whenever you want to upload (I used uploadTask method of URLSession).

Create uploadTask and start uploading.

You will also have to make your own protocol methods that are called by normal protocol methods of URLSession, to send your custom sub class instead of URLSession object to the delegate you want.

Then in that delegate, you may check for the information of indexPath that is stored in the custom sub class you got from the previous step and update the appropriate cell.

The same could be achieved by using Notifications I guess.

Below is the screenshot of the sample application I wrote:

enter image description here

public class TestURLSession:NSObject, URLSessionTaskDelegate {

    var cellIndexPath:IndexPath!
    var urlSession:URLSession!
    var urlSessionUploadTask:URLSessionUploadTask!
    var testUrlSessionDelegate:TestURLSessionTaskDelegate!

    init(configuration: URLSessionConfiguration, delegate: TestURLSessionTaskDelegate?, delegateQueue queue: OperationQueue?, indexPath:IndexPath){
        super.init()
        self.urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: queue)
        self.cellIndexPath = indexPath
        self.testUrlSessionDelegate = delegate
    }

    func uploadTask(with request: URLRequest, from bodyData: Data) -> URLSessionUploadTask{
        let uploadTask = self.urlSession.uploadTask(with: request, from: bodyData)
        self.urlSessionUploadTask = uploadTask
        return uploadTask
    }


    public func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64){
        self.testUrlSessionDelegate.urlSession(self, task: self.urlSessionUploadTask, didSendBodyData: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend)
    }

}

protocol TestURLSessionTaskDelegate : URLSessionDelegate {

    func urlSession(_ session: TestURLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)

}

Edits are welcome.

kerry
  • 2,362
  • 20
  • 33
  • This solution won't work as i already said in my question, if i use reloadRowAtIndexPath method to update cell info then cell continuously blinks. – Dhaval Dobariya Sep 23 '16 at 06:20
  • I do not think I understood you. In my answer, I am no where reloading the row. What you said is right that if you reload the row again and again, it will flicker but here just like I updated the label text without reloading, you can use UIProgressBiew's setProgressbar function to update progress. You will not need to reload. Let me know if you need some clarifications. – kerry Sep 23 '16 at 06:48
  • Typo : it is setProgress function of UIProgressView – kerry Sep 23 '16 at 06:54
  • Yes @kerry, you don't get me. I am trying to say i want to show update progress like whatsapp uploading and downloading progress, so i think UIProgressView don't suitable in my requirement. And one most important thing in my question i am asking how to update UITableView cell control's value each time i get update from NSURLSession delegate methods. – Dhaval Dobariya Sep 30 '16 at 04:52
  • Regarding your first comment, I agree that UIProgressView won't work for you. May be this will work https://github.com/matibot/MBCircularProgressBar. Secondly, what I am trying to say is that you do not have to update the whole cell, all you have to do is find out that cell, then find out that circular control of that custom cell and call update method of that control rather than updating UITableViewCell. This way you can update and the cell won't flicker. – kerry Sep 30 '16 at 11:24
  • @kerry , I need your help following issue. https://stackoverflow.com/q/46047271/2910061 can you please check this link. appreciate your any help – Ilesh P Sep 06 '17 at 06:33
  • Yes tell me @ilesh – kerry Sep 06 '17 at 06:34
  • @kerry Please check above link and give some feedback and if you want more than please ask me. – Ilesh P Sep 06 '17 at 08:50
0

Here the solution which i applied, may be helpful to someone who wants same implementations as i want, without using third party library or classes.

I have created one custom UIView and design circular progress indicator using CALayer and some animations. This is not a big deal. But the thing which is difficult for me is i want this progress indicator in several cells which indicates multiple image progress in percentages.

So i have created one custom class with properties like

@property (nonatomic,retain) NSIndexPath              *indexPath;
@property (nonatomic,strong) NSURLSessionTask         *uploadtask;

Then i maintain one NSMutableArray which contains my custom class objects which has values for each uploadTask for currently uploading images and merged string which contains indexPath. Now i have track of my all uploading images so i have change uploaded percentage in my custom progress indicator UIView with help of indexPath values whenever i receive response from delegate method of NSURLSession.

Dhaval Dobariya
  • 171
  • 1
  • 12
  • Hello @Dhaval Dobariya can you please help me to fix this issue https://stackoverflow.com/q/46047271/2910061 ? – Ilesh P Sep 06 '17 at 00:05
0

I had a similar stuff to do where in which I wanted to download files and show progress bar. My idea was to create a Custom object which keep track of a particular download and all the cells will internally listen to the changes in this object. Each cell will have its own object uniquely identified by the task identifier. I have written a sample code in Swift 3 available in the below link (skeleton code also added)

FileDownloader

class DownLoadData: NSObject {
    var fileTitle: String = ""
    var downloadSource: String = ""
    var downloadTask: URLSessionDownloadTask?
    var taskResumeData: Data?
    var downloadProgress: Float = 0.0
    var isDownloading: Bool = false
    var isDownloadComplete: Bool = false
    var taskIdentifier: Int = 0
    var groupDownloadON:Bool = false
    var groupStopDownloadON:Bool = false

    init(with title:String, and source:String){
        self.fileTitle = title
        self.downloadSource = source
        super.init()

    }

    func startDownload(completion:@escaping (Result<Bool, Error>)->Void,progressHandler:@escaping (Float)->Void ){

    }
    func resumeDownload(completion:@escaping (Result<Bool, Error>)->Void,progressHandler:@escaping (Float)->Void ){


    }

    func pauseDownload(){

    }

    func stopDownload(){

        if self.isDownloading{

        }
    }
    func cleanUpHandlers(){

        // remove the completion handlers from the network manager as resume is taken as a new task.

    }
    func handleDownloadSuccess(){

    }
    func handleDownloadError(){


    }
}
anoop4real
  • 7,598
  • 4
  • 53
  • 56
-1

Use URLSessionTaskDelegate method and do necessary calculation:

func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
Yamen Nassif
  • 2,416
  • 2
  • 24
  • 48
-1

Below is a solution, that is tested for a single file upload. But you can modify it to support multiple file uploads. Make sure to add necessary IBOutlets and IBAction as necessary. The image is added in 'Assets.xcassets'.

My UI looks like below:

enter image description here

Below is the code for ViewController.

import UIKit

class UploadProgressViewController: UIViewController, URLSessionTaskDelegate {

@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var progressView: UIProgressView!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    progressView.progress = 0.0
}

@IBAction func didTapOnStartUploadButton(_ sender: Any) {
    startDownload()
}

func startDownload () {
    // 1. Prepare data to download
    var data = Data()
    if let image = UIImage(named: "swift.jpg") {
        data = image.pngData()!
    }
    // 2. Creation of request
    var request = URLRequest(url: NSURL(string: "http://127.0.0.1:8000/swift.png")! as URL)
    request.httpMethod = "POST"
    request.setValue("Keep-Alive", forHTTPHeaderField: "Connection")
    // 3. Configuring the Session
    let configuration = URLSessionConfiguration.default
    let mainqueue = OperationQueue.main
    // 4. Start the upload task
    let session = URLSession(configuration: configuration, delegate:self, delegateQueue: mainqueue)
    let dataTask = session.uploadTask(with: request, from: data)
    dataTask.resume()
}

// URLSessionTaskDelegate Handling

func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
    let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
    print("session \(session) uploaded \(uploadProgress * 100)%.")
    progressView.progress = uploadProgress
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    print(error.debugDescription)
}
Nish
  • 545
  • 5
  • 7
  • Please read the question carefully, where is your UITableView and UITableiViewCell, because for one image its not complex. But thanks for the reply!!! – Dhaval Dobariya Oct 22 '18 at 03:56