如何通过urlsession更新uitableview单元中的progressview(下载/上传文件) [英] How to update progressview in uitableview cell by urlsession (download/upload file)

查看:62
本文介绍了如何通过urlsession更新uitableview单元中的progressview(下载/上传文件)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Objective c中,我通过AFNETWORKING在uitableviewcell(多次上传/下载)中编写了带有progressview的下载/上传.工作发现它可以是update progressview/file/cell.

In Objective c i wrote download/upload with progressview in uitableviewcell (multiple upload/download) by AFNETWORKING. and work find it's can be update progressview/file/cell.

,现在,我新手第一次更改为SWIFT编程,并使用urlsession.

and Now im noob first time change to SWIFT programming and use urlsession.

代码

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, URLSessionDelegate, URLSessionDataDelegate, URLSessionTaskDelegate {

    //var dataArr:Dictionary<String,String> = [:]
    var dataArr : NSMutableArray = NSMutableArray.init()
    var myTableview:UITableView = UITableView.init()
    let color = UIColor(red: 69/255, green: 57/255, blue: 169/255, alpha: 1.0)
    let cellID: String = "customCell"
    var progressBar : UIProgressView = UIProgressView.init()
    let progressView : UIView = UIView.init(frame: CGRect(x: 100, y: 10, width: 100, height: 20))

    func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
    {
        let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
        print(uploadProgress)

        DispatchQueue.main.async {

            self.progressBar.progress = uploadProgress
        }
    }

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

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

        let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)

        let textName: UILabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
        textName.textColor=UIColor.black
        textName.backgroundColor=UIColor.green
        textName.text=dataArr[indexPath.row] as? String
        print("\(dataArr[indexPath.row])");
        textName.font=UIFont.systemFont(ofSize: 14)
        cell.addSubview(textName)


        progressView.backgroundColor = UIColor.red
        progressView.tag=indexPath.row

        let customKeys=["type","Facebook","Google","Twitter"];
        let customsValues=["uploadFile","Mark","Lary","Goo"];
        let customDatas=Dictionary(uniqueKeysWithValues: zip(customKeys,customsValues))


        progressBar = UIProgressView.init(frame: CGRect(x: 0, y: 5, width: 100, height: 20))
        progressBar.tag=indexPath.row
        progressView.addSubview(progressBar)

        cell.addSubview(progressView)

        uploadImage(data_dict: customDatas, uploadImg: dataArr[indexPath.row] as! String)



        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

        let hCell:CGFloat = 50.0

        return hCell
    }

    //    override func viewWillAppear(_ animated: Bool) {
    //        super.viewWillAppear(animated)
    //        setNeedsStatusBarAppearanceUpdate()
    //    }
    override var preferredStatusBarStyle: UIStatusBarStyle {
        // Change font of status bar is white.
        .lightContent

    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        dataArr=["1.jpg","2.jpg","3.jpg"]
        //print(dataArr)
        let myScreen = UIScreen.main.bounds
        let statusHieght = UIApplication.shared.statusBarFrame.height

        if #available(iOS 13.0, *) {
            let app = UIApplication.shared
            let statusBarHeight: CGFloat = app.statusBarFrame.size.height

            let statusbarView = UIView()
            statusbarView.backgroundColor = color
            statusbarView.tintColor = .white
            view.addSubview(statusbarView)


            statusbarView.translatesAutoresizingMaskIntoConstraints = false
            statusbarView.heightAnchor
                .constraint(equalToConstant: statusBarHeight).isActive = true
            statusbarView.widthAnchor
                .constraint(equalTo: view.widthAnchor, multiplier: 1.0).isActive = true
            statusbarView.topAnchor
                .constraint(equalTo: view.topAnchor).isActive = true
            statusbarView.centerXAnchor
                .constraint(equalTo: view.centerXAnchor).isActive = true

        } else {
            let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
            statusBar?.backgroundColor = color
        }

        UINavigationBar.appearance().barTintColor = color
        UINavigationBar.appearance().tintColor = .white
        UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
        UINavigationBar.appearance().isTranslucent = false

        let navBar = UINavigationBar(frame: CGRect(x: 0, y: statusHieght, width: myScreen.size.width, height: 44))

        //navBar.isTranslucent=true
        //navBar.backgroundColor = .red
        let navItem = UINavigationItem(title: "SomeTitle")
        let doneItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target:nil , action:#selector(ClickDone))
        navItem.rightBarButtonItem = doneItem
        navBar.setItems([navItem], animated: false)
        self.view.addSubview(navBar)

        let AllTopDistance=statusHieght+navBar.frame.size.height

        let myView:UIView = UIView.init(frame: CGRect(x: 0, y: AllTopDistance, width: myScreen.size.width, height: myScreen.size.height-AllTopDistance))
        myView.backgroundColor = .lightGray
        myTableview=UITableView.init(frame: CGRect(x: 0, y: 0, width: myScreen.size.width, height: myScreen.size.height-AllTopDistance))
        myTableview.register(UITableViewCell.self, forCellReuseIdentifier: cellID)

        print("\(statusHieght) \(myScreen.size.width) \(AllTopDistance)")
        myTableview.delegate=self
        myTableview.dataSource=self
        myTableview.backgroundColor=UIColor.red

        myView.addSubview(myTableview)

        self.view.addSubview(myView)
    }

    @objc func ClickDone(){
        print("Done")
    }


    func calculateTopDistance() -> CGFloat{

        /// Create view for misure
        let misureView : UIView     = UIView()
        misureView.backgroundColor  = .clear
        view.addSubview(misureView)

        /// Add needed constraint
        misureView.translatesAutoresizingMaskIntoConstraints                    = false
        misureView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive     = true
        misureView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive   = true
        misureView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        if let nav = navigationController {
            misureView.topAnchor.constraint(equalTo: nav.navigationBar.bottomAnchor).isActive = true
        }else{
            misureView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        }

        /// Force layout
        view.layoutIfNeeded()

        /// Calculate distance
        let distance = view.frame.size.height - misureView.frame.size.height

        /// Remove from superview
        misureView.removeFromSuperview()

        return distance

    }


    @objc func uploadImage(data_dict : Dictionary<String,String>, uploadImg : String)
    {
        print("click \(data_dict)")
        let image = UIImage(named: uploadImg)

        // generate boundary string using a unique per-app string
        let boundary = UUID().uuidString


        let config = URLSessionConfiguration.default
        //let session = URLSession(configuration: config)

        let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)

        // Set the URLRequest to POST and to the specified URL
        var urlRequest = URLRequest(url: URL(string: "http://x.x.x.x/xxx/Labs.php")!)
        urlRequest.httpMethod = "POST"

        // Set Content-Type Header to multipart/form-data, this is equivalent to submitting form data with file upload in a web browser
        // And the boundary is also set here
        urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        var data = Data()

        for (key, value) in data_dict {
            print(key, value)
            let fieldName = key
            let fieldValue = value
            data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
            data.append("Content-Disposition: form-data; name=\"\(fieldName)\"\r\n\r\n".data(using: .utf8)!)
            data.append("\(fieldValue)".data(using: .utf8)!)

        }

        // Add the image data to the raw http request data
        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
        data.append("Content-Disposition: form-data; name=\"fileToUpload\"; filename=\"\(uploadImg)\"\r\n".data(using: .utf8)!)
        data.append("Content-Type: image/png\r\n\r\n".data(using: .utf8)!)
        data.append((image?.jpegData(compressionQuality: 1.0))!)

        // End the raw http request data, note that there is 2 extra dash ("-") at the end, this is to indicate the end of the data
        // According to the HTTP 1.1 specification https://tools.ietf.org/html/rfc7230
        data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

        // Send a POST request to the URL, with the data we created earlier
        session.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in

            if(error != nil){
                print("\(error!.localizedDescription)")
            }

            guard let responseData = responseData else {
                print("no response data")
                return
            }

            if let responseString = String(data: responseData, encoding: .utf8) {
                print("Response data : \(responseString)")
            }
        }).resume()
    }    
}

??如何通过单元格/文件在uitableview中定义URLSESSION更新进度视图谢谢.

?? How define URLSESSION update progressview in uitableview by cell/file Thank you.

推荐答案

我很好奇您是如何使用AFNetworking在Objective-C中完成的.至少从概念上讲,使用URLSession在Swift中的实现应该没有太大的区别.

I'm curious how you did it in Objective-C with AFNetworking. At least conceptional, there shouldn't be a big difference to implementation in Swift with URLSession.

我不知道更新进度的主要问题是,与所有单元格的单个UIView实例共享progressView变量.

Imho your main problem about updating your progress is, that you share the progressView variable with a single UIView instance for all of your cells.

  1. 您没有为每个单元格初始化一个新的progressView,而是为所有单元格共享一个视图
  2. 由于1, cell.addSubview(progressView)不仅将您的progressView添加到该单元格,还从其他单元格中删除了progressView,因为一个视图只能有一个父级视图.
  3. 您的progressView将有多个UIProgressBars作为子视图.每次调用 tableView(_:cellForRowAt indexPath:)
  4. 使用 self.progressBar.progress = uploadProgress ,您将始终更新上次初始化的progressBar,因为您没有对其他控件的引用.
  1. you do not initialize a new progressView for each cell, but share one view for all cells
  2. because of 1, cell.addSubview(progressView) doesn't only add your progressView to that cell, it removes your progressView from the other cells as well, because a view can only have one parent view.
  3. your progressView will have multiple UIProgressBars as subview. One for each time tableView(_:cellForRowAt indexPath:) is called
  4. with self.progressBar.progress = uploadProgress you will always update the progressBar which was last initialized, because you don't have a reference to the other ones.


为了使它以一种干净的方式工作,我建议您对MVVM架构进行一些研究.


To get this to work in a clean way, I'd recommend you do some research to MVVM architecture.

  1. 为您的单元格创建一个UITableViewCell子类
  2. 为该单元格创建一个ViewModel类
  3. 将每个单元格的viewModel实例存储在viewController中(或者最好将其存储在单独的TableViewDataSource对象中)
  4. 在ViewModel中实现URLSessionDelegate协议,并将适当的viewModel实例设置为您的uploadTasks的委托


快速而又肮脏的修补程序:


For a quick and dirty fix:

删除这些行:

var progressBar : UIProgressView = UIProgressView.init()
let progressView : UIView = UIView.init(frame: CGRect(x: 100, y: 10, width: 100, height: 20))  

添加变量:

var uploadTasks: [URLSessionDataTask: IndexPath] = [:]

添加帮助函数以计算viewTag:

Add a helper function to calculate viewTag:

func viewTag(for indexPath: IndexPath) -> Int {
    return indexPath.row + 1000
}

将您的 tableView(_:cellForRowAt indexPath:)更改为:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)

    let textName: UILabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
    textName.textColor=UIColor.black
    textName.backgroundColor=UIColor.green
    textName.text=dataArr[indexPath.row] as? String
    print("\(dataArr[indexPath.row])");
    textName.font=UIFont.systemFont(ofSize: 14)
    cell.addSubview(textName)

    let progressView = UIView(frame: CGRect(x: 100, y: 10, width: 100, height: 20))
    progressView.backgroundColor = UIColor.red

    let customKeys=["type","Facebook","Google","Twitter"];
    let customsValues=["uploadFile","Mark","Lary","Goo"];
    let customDatas=Dictionary(uniqueKeysWithValues: zip(customKeys,customsValues))


    let progressBar = UIProgressView.init(frame: CGRect(x: 0, y: 5, width: 100, height: 20))
    progressBar.tag = viewTag(for: indexPath)
    progressView.addSubview(progressBar)

    cell.addSubview(progressView)

    uploadImage(data_dict: customDatas, indexPath: indexPath)

    return cell
}

将您的uploadImage方法更改为:

Change your uploadImage method to:

@objc func uploadImage(data_dict : Dictionary<String,String>, indexPath : IndexPath) {
    print("click \(data_dict)")
    let uploadImg = dataArr[indexPath.row] as! String
    let image = UIImage(named: uploadImg)

    ...

    let task = session.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in
        ...
    })

    uploadTasks[task] = indexPath
    task.resume()
}

将urlSession委托方法更改为:

Change your urlSession delegate method to:

func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
    let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
    print(uploadProgress)

    guard let indexPath = uploadTasks[task] else { return }
    let viewTag = viewTag(for: indexPath)
    guard let progressBar = self.view.viewWithTag(viewTag) as? UIProgressView else { return }

    DispatchQueue.main.async {
        progressBar.progress = uploadProgress
    }
}

这篇关于如何通过urlsession更新uitableview单元中的progressview(下载/上传文件)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆