如何在细胞中显示进展?

时间:2017-08-23 10:03:40

标签: ios swift uitableview

我使用didSelectRowAt indexPath:中的代码在单击单击时启动下载文件,并使用cellForRowAt indexPath:中的代码显示正在下载文件的进度。

class TableViewController: UITableViewController {

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

        return (mainArray[buttonIndex] as AnyObject).count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: String(format: "cell", indexPath.row), for: indexPath)
        let circularProgressViewForCell = FFCircularProgressView(frame: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(24), height: CGFloat(24)))
        cell.accessoryView = circularProgressViewForCell
        circularProgressViewForCell.isHidden = true

        DownloadManager.shared.onProgress = { (progress) in
        circularProgressViewForCell.isHidden = false
        OperationQueue.main.addOperation {
            circularProgressViewForCell.progress = CGFloat(progress)
            if (cell.accessoryView as! FFCircularProgressView?)?.progress == 1.0 {
                print("FFCircularProgressView")
                (cell.accessoryView as! FFCircularProgressView?)?.circularState = .completed
            }
        }
    }
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let url = URL(string: "link")!
    let downloadTaskLocal =  DownloadManager.shared.activate().downloadTask(with: url)
    downloadTaskLocal.resume()
}
}

我在DownloadManager创建了URLSession:

import Foundation

class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {

    static var shared = DownloadManager()

    typealias ProgressHandler = (Float) -> ()

    var onProgress : ProgressHandler? {
        didSet {
            if onProgress != nil {
                let _ = activate()
            }
        }
    }

    override private init() {
        super.init()
    }

    func activate() -> URLSession {
        let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background")


        return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
    }

    private func calculateProgress(session : URLSession, completionHandler : @escaping (Float) -> ()) {
        session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
            let progress = downloads.map({ (task) -> Float in
                if task.countOfBytesExpectedToReceive > 0 {
                    return Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive)
                } else {
                    return 0.0
                }
            })
            completionHandler(progress.reduce(0.0, +))
        }
    }

    func urlSession(_ session: URLSession,
                    downloadTask: URLSessionDownloadTask,
                    didFinishDownloadingTo location: URL){

        let fileName = downloadTask.originalRequest?.url?.lastPathComponent
        let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
        let documentDirectoryPath:String = path[0]
        let fileManager = FileManager()
        var destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appending("/\(id)"))
        do {
            try fileManager.createDirectory(at: destinationURLForFile, withIntermediateDirectories: true, attributes: nil)
            destinationURLForFile.appendPathComponent(String(describing: fileName!))
            try fileManager.moveItem(at: location, to: destinationURLForFile)
        }catch(let error){
            print(error)
        }

    }

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {

        if totalBytesExpectedToWrite > 0 {
            if let onProgress = onProgress {
                calculateProgress(session: session, completionHandler: onProgress)
            }
            let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
            debugPrint("Progress \(downloadTask) \(progress)")

        }
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        debugPrint("Task completed: \(task), error: \(error)")
    }

}

我想点击单元格,启动文件下载并显示所选单元格中下载文件的进度。我可以在didSelectRowAt indexPath:中创建我的progressView,但它不是最佳解决方案,因为用户可能会移动我的应用中的上一个或下一个控制器,或者可能会在下载处于活动状态时隐藏应用。当用户在TableViewController中返回时,我更新了cellForRowAt indexPath:

中的进度

问题:当我开始下载文件时,我的进度不会显示在选定的行中。进度显示在屏幕外的行中。当我滚动tableView时,我看到所有单元格中的进度都在变化。如何解决它并显示所选单元格的进度?

UPD

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

                    var item = self.items[indexPath.row]
                    if item.downloadStatus == .inProgress || item.downloadStatus == .completed {
                        print("video already downloaded")
                    }
                    else {
                        let url = URL(string: "link\(indexPath.row)")!

                        let int = self.tableId + indexPath.row // table0 = 1000, table1 = 2000 and etc.

                        DownloadManager.shared.identifier = int
                        let downloadTaskLocal =  DownloadManager.shared.activate().downloadTask(with: url)
                        downloadTaskLocal.resume()

                        DownloadManager.shared.onProgress = { (row, progress) in

                            var row = row
                            row = row - self.tableId


                            DispatchQueue.main.async {
                                let indexpath = IndexPath.init(row: row, section: 0)
                                let cell = self.tableView.cellForRow(at: indexpath)
                                print("downloading for cell \(String(describing: cell?.tag))")
                                if progress <= 1.0 {

                                    let progressRing = cell?.accessoryView as! FFCircularProgressView
                                    progressRing.progress = CGFloat(progress)

                                    if progress == 1.0 {
                                        item.downloadStatus = .completed
                                        cell?.textLabel?.text = "Download Complete"
                                    }
                                    else {
                                        cell?.textLabel?.text = "Download In Progress"
                                    }

                                }
                            }



}

UPD 1

import UIKit
import StoreKit

enum DownloadStatus {
    case none
    case inProgress
    case completed
    case failed
}
struct item {
    var title : String!
    let link = ""
    var downloadStatus : DownloadStatus = .none

    init(title: String) {
        self.title = title
    }
}

var id = ""

class MasterViewController: UITableViewController {

    var mainArray:[[Any]] = []
    var index = 0
    var buttonIndex = 0
    var objects = [Any]()
    var items = [item]()
    var tableId = 0

    override func viewDidLoad() {
        super.viewDidLoad()

            items.append(item(title: "Video 1"))
            items.append(item(title: "Video 2"))
            items.append(item(title: "Video 3"))
            items.append(item(title: "Video 4"))
            items.append(item(title: "Video 5"))
            items.append(item(title: "Video 6"))
            items.append(item(title: "Video 7"))
            items.append(item(title: "Video 8"))
            items.append(item(title: "Video 9"))
            items.append(item(title: "Video 10"))
            items.append(item(title: "Video 11"))
            items.append(item(title: "Video 12"))

        if buttonIndex == 0 {
            id = "id0"
            tableId = 1000
        }
        else if buttonIndex == 1 {
            id = "id1"
            tableId = 2000
        }

        mainArray = [array0,array1,array2,array3,array4,array5,array6]

        NotificationCenter.default.addObserver(self, selector: #selector(self.onApplicationDidBecomeActiveNotification), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)

    }

    func onApplicationDidBecomeActiveNotification(notification:Notification) {
        self.tableView.reloadData()
    }

    override func viewWillAppear(_ animated: Bool) {
        self.tableView.reloadData()
    }

    override func viewDidAppear(_ animated: Bool) {
        self.tableView.reloadData()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {

        return 1
    }

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

        return (mainArray[buttonIndex] as AnyObject).count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: String(format: "cell", indexPath.row), for: indexPath)

        let item = items[indexPath.row]

        print(item.downloadStatus)

        if item.downloadStatus != .completed {
            let progressRing = FFCircularProgressView(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
            cell.tag = indexPath.row
            cell.accessoryType = UITableViewCellAccessoryType.none
            cell.accessoryView = progressRing
        }
        else {
            cell.accessoryView = nil
        }

        return cell
    }



    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

                        var item = self.items[indexPath.row]
                        if item.downloadStatus == .inProgress || item.downloadStatus == .completed {
                            print("video already downloaded")
                        }
                        else {
                            let url = URL(string: "link\(indexPath.row + 1).mp3")!
                            let int = self.tableId + indexPath.row
                            let downloadManager = DownloadManager()
                            downloadManager.identifier = int
                            downloadManager.folderPath = id
                            let downloadTaskLocal =  downloadManager.activate().downloadTask(with: url)
                            downloadTaskLocal.resume()


                             downloadManager.onProgress = { (row, progress) in

                                var row = row
                                row = row - self.tableId

                                DispatchQueue.main.async {
                                    let indexpath = IndexPath.init(row: row, section: 0)
                                    let cell = self.tableView.cellForRow(at: indexpath)
                                    print("downloading for cell \(String(describing: cell?.tag))")
                                    if progress <= 1.0 {

                                        let progressRing = FFCircularProgressView(frame: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(24), height: CGFloat(24)))
                                        cell?.accessoryView = progressRing
                                        progressRing.progress = CGFloat(progress)

                                        if progress == 1.0 {
                                            item.downloadStatus = .completed
                                        }

                                    }


    }               
    }
}

FirstViewController的代码

import UIKit
import StoreKit

class TableViewController: UITableViewController {

    let array = ["","","","","","",""]


    override func viewDidLoad() {
        super.viewDidLoad()

        }

    override func viewWillAppear(_ animated: Bool) {
        let newBackButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
        self.navigationItem.backBarButtonItem = newBackButton
        self.tableView.reloadData()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {

        return 1
    }

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

        return array.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell

        cell.button.tag = indexPath.row

        cell.selectionStyle = .none

        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    }

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

        return 165.0
    }

    public override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 12.0
    }

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
    {
        let headerView = UIView()
            headerView.backgroundColor = UIColor.clear

        return headerView
    }

    // MARK: - Navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        if (segue.destination is MasterViewController) {
            if let button = sender as? UIButton {
                (segue.destination as? MasterViewController)?.buttonIndex = button.tag
            }
        }
    }
}

TableViewCell的代码

import UIKit

class TableViewCell: UITableViewCell {

    @IBOutlet weak var button: UIButton!

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)


    }

}

4 个答案:

答案 0 :(得分:2)

@Artem我在DownloadManager中添加了一个属性,作为&#39;标识符&#39; (INT)。这将保存indexpath.row值。

在ProgressHandler完成块中发回此标识符值。

附上上述更改的更新代码。

static var shared = DownloadManager()
var identifier : Int = -1
typealias ProgressHandler = (Int, Float) -> ()

var onProgress : ProgressHandler? {
    didSet {
        if onProgress != nil {
            let _ = activate()
        }
    }
}

override private init() {
    super.init()
}

func activate() -> URLSession {
    let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background")


    return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
}

private func calculateProgress(session : URLSession, completionHandler : @escaping (Int, Float) -> ()) {
    session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
        let progress = downloads.map({ (task) -> Float in
            if task.countOfBytesExpectedToReceive > 0 {
                return Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive)
            } else {
                return 0.0
            }
        })
        completionHandler(self.identifier, progress.reduce(0.0, +))
    }
}

func urlSession(_ session: URLSession,
                downloadTask: URLSessionDownloadTask,
                didFinishDownloadingTo location: URL){

    let fileName = downloadTask.originalRequest?.url?.lastPathComponent
    let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
    let documentDirectoryPath:String = path[0]
    let fileManager = FileManager()
    var destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appending("/\(id)"))
    do {
        try fileManager.createDirectory(at: destinationURLForFile, withIntermediateDirectories: true, attributes: nil)
        destinationURLForFile.appendPathComponent(String(describing: fileName!))
        try fileManager.moveItem(at: location, to: destinationURLForFile)
    }catch(let error){
        print(error)
    }

}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {

    if totalBytesExpectedToWrite > 0 {
        if let onProgress = onProgress {
            calculateProgress(session: session, completionHandler: onProgress)
        }
        let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
        debugPrint("Progress \(downloadTask) \(progress)")

    }
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    debugPrint("Task completed: \(task), error: \(String(describing: error))")


   }

}

在您的DownloadManager中

类DownloadManager:NSObject,URLSessionDelegate,URLSessionDownloadDelegate {

pragma integrity_check

更新的答案

我创建了一个简单的项目来演示UITableViewCell上的下载进度更新。

https://github.com/mcabasheer/table-cell-progress-bar/tree/master

enter image description here

希望这会有所帮助:)

答案 1 :(得分:0)

您的案例中的问题是细胞的重复使用 在tableView(: cellForRowAt:)中,您必须管理用户从具体单元格下载而不是下载的事实 您的DownloadManager似乎不知道您要从哪个网址(单元格)下载。
例如,保留所选网址的数组。

答案 2 :(得分:0)

对于渐进式下载并向tableview显示,您必须按照以下步骤操作

步骤1 :单击单元格时,将其索引存储在数组中。 (我们将该名称命名为arrDownloads

第2步:在cellForRowAtIndexPath中检查arrDownloads是否包含当前索引路径

第3步:如果cellForRowAtIndexPath中的当前索引位于arrDownloads内,则将当前索引的URL传递给下载管理器。 (确保您有另一组URL来下载图像(文件))

This可能会有所帮助。

答案 3 :(得分:0)

我想,你在tableView的标题中设置你的进度。滚动桌面视图时,您可以看到进度。