简体   繁体   中英

swift: Label in collectionView cell

I have 7 sections and 3 cels in section in my collectionView.

My code to download file when cell was clicked:

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

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

var downloadQ = [Int: [Int]]()

typealias ProgressHandler = (Int, Float) -> ()
var items = [[item]]()
var tableId = 0
var onProgress : ProgressHandler?

override func viewDidLoad() {
        super.viewDidLoad()

items = [
        [item(title: "item 2"),item(title: "item 2"),item(title: "item 2")],
        [item(title: "item 2"),item(title: "item 2"),item(title: "item 2")],
        [item(title: "item 2"),item(title: "item 2"),item(title: "item 2")],
        [item(title: "item 2"),item(title: "item 2"),item(title: "item 2")],
        [item(title: "item 2"),item(title: "item 2"),item(title: "item 2")],
        [item(title: "item 2"),item(title: "item 2"),item(title: "item 2")],
        [item(title: "item 2"),item(title: "item 2"),item(title: "item 2")]
    ]
}

func numberOfSections(in collectionView: UICollectionView) -> Int {
        return media.count
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return media[section].count
    }

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

var item = self.items[indexPath.section][indexPath.row]
                    downloadQ[indexPath.section]?.append(indexPath.row)

                    var theArray = downloadQ[indexPath.section] ?? [Int]()
                    theArray.append(indexPath.row)
                    downloadQ[indexPath.section] = theArray

                    // Create the actions

                    let url = URL(string: "link")!

                    let downloadManager = DownloadManager()
                    downloadManager.identifier = indexPath.row
                    downloadManager.tableId = indexPath.section
                    downloadManager.folderPath = "folder"
                    let downloadTaskLocal =  downloadManager.activate().downloadTask(with: url)
                    downloadTaskLocal.resume()

                    var cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MasterViewCell
                    cell = self.collectionView?.cellForItem(at: indexPath) as! MasterViewCell
                    cell.label?.isHidden = false

                    downloadManager.onProgress = { (row, tableId, progress) in

                        DispatchQueue.main.async {
                            let appDelegate = UIApplication.shared.delegate as! AppDelegate

                            if appDelegate.masterVC == nil {
                                return
                            }

                            if appDelegate.masterVC.tableId != tableId {
                                return
                            }

                            let indexpath = IndexPath.init(row: row, section: indexPath.section)
                            var cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MasterViewCell
                            cell = appDelegate.masterVC.collectionView.cellForItem(at: indexpath) as! MasterViewCell

                            cell.label?.text = "\(CGFloat(progress))%"

                        }

                    }
}

My Download manager class:

import Foundation
import UIKit

extension URLSession {
    func getSessionDescription () -> Int {
        // row id
        return Int(self.sessionDescription!)!
    }

    func getDebugDescription () -> Int {
        // table id
        return Int(self.debugDescription)!
    }
}

class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {



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


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

    override init() {
        super.init()
    }

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

        let urlSession = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
        urlSession.sessionDescription = String(identifier)
        urlSession.accessibilityHint = String(tableId)

        return urlSession
    }

    private func calculateProgress(session : URLSession, completionHandler : @escaping (Int, 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
                }
            })

            //print("tbale id \(session.accessibilityHint ?? "hit")")

            let stringNumb = (session.accessibilityHint ?? "hit")
            let someNumb = Int(stringNumb as String) // 1357 as integer

            let string1 = (session.sessionDescription ?? "hit")
            let some1 = Int(string1 as String) // 1357 as integer

            if let idx = downloadQ[someNumb!]?.index(of: some1!) {
                downloadQ[someNumb!]?.remove(at: idx)
                //print("remove:\(downloadQ)")
            }

            completionHandler(session.getSessionDescription(), Int(session.accessibilityHint!)!, progress.reduce(0.0, +))
        }
    }

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

        let stringNumb = (session.accessibilityHint ?? "hit")
        let someNumb = Int(stringNumb as String) // 1357 as integer

        let string1 = (session.sessionDescription ?? "hit")
        let some1 = Int(string1 as String) // 1357 as integer

        if let idx = downloadQ[someNumb!]?.index(of: some1!) {
            downloadQ[someNumb!]?.remove(at: idx)
            //print("remove:\(downloadQ)")
        }

        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("/\(folderPath)"))


        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) {

        let stringNumb = (session.accessibilityHint ?? "hit")
        let someNumb = Int(stringNumb as String) // 1357 as integer

        let string1 = (session.sessionDescription ?? "hit")
        let some1 = Int(string1 as String) // 1357 as integer

        if let idx = downloadQ[someNumb!]?.index(of: some1!) {
            downloadQ[someNumb!]?.remove(at: idx)
            //print("remove:\(downloadQ)")
        }

        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?) {

        let stringNumb = (session.accessibilityHint ?? "hit")
        let someNumb = Int(stringNumb as String) // 1357 as integer

        let string1 = (session.sessionDescription ?? "hit")
        let some1 = Int(string1 as String) // 1357 as integer

        if let idx = downloadQ[someNumb!]?.index(of: some1!) {
            downloadQ[someNumb!]?.remove(at: idx)
            //print("remove:\(downloadQ)")
        }

        //debugPrint("Task completed: \(task), error: \(String(describing: error))")
    }

}

But I have a problem. My app crashes in this line - cell = self.collectionView?.cellForItem(at: indexPath) as! MasterViewCell

with this issue: Fatal error: Unexpectedly found nil while unwrapping an Optional value. How to fix it?

The problem you are facing is due to cell reuse. Each time you scroll in your collection or table, you usually deque the old cell. Which means whenever you are displaying new cell cellForRow (table), or itemForRow (collection), you'll have to have data for the cell to be able to feed it.

If you only store the data about your download process into the cell you'll lose them at the moment when prepareForReuse: , system call, gets called, because the cell gets reset.

The other way around would be to use static cells. Because the static cells, never gets reset. But that's handy only when you use a small number of items on the screen. If you have tens or hundreds of cells, then it gets very costly on performance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM