2

Ive been searching for a answer to this one for days now and cant seem to figure it out. I have a Collection View with custom cell. When you double tap a cell in the Collection View it will either download a file or delete it if its been downloaded before.

During the download a progress bar displays the progress of the download then displays a small icon in the top left corner. When deleting it removes the icon.

enter image description here

If you download from one cell and delete from another while first download is in progress it works fine but only if both cells were visible within the collection view.

enter image description here

if i download from one cell, then scroll offscreen and delete from a cell that is not in same screen as the cell that is being download from, it removes the corner image as usual then displays the progress bar of the cell that is being download from.

enter image description here

I don't know if this is an error with how i am reusing cells??? It doesn't seem to have anything to do with how i am updating the cell or collection view which works in all cases except after scrolling.

Below is 2 functions that download or delete file:

func downloadDataToDevice(cell: JourneyCollectionViewCell, selectedIndexPath: IndexPath){

    let downloadedAudio = PFObject(className: "downloadedAudio")
    // save all files with unique name / object id
    let selectedObjectId = self.partArray[selectedIndexPath.item].id
    let selectedPartName = self.partArray[selectedIndexPath.item].name

    let query = PFQuery(className: "Part")
    query.whereKey("objectId", equalTo: selectedObjectId)
    query.getFirstObjectInBackground { (object, error) in

        if error != nil || object == nil {
            print("No object for the index selected.")
        } else {
            //print("there is an object, getting the file.")
            downloadedAudio.add(object?.object(forKey: "partAudio") as! PFFile, forKey: selectedPartName)
            let downloadedFile = object?.object(forKey: "partAudio") as! PFFile

            // get the data first so we can track progress
            downloadedFile.getDataInBackground({ (success, error) in
                if (success != nil) {
                    // pin the audio if there is data
                    downloadedAudio.pinInBackground(block: { (success, error) in
                        if success {

                            // reload the cell
                            self.reloadCell(selectedIndexPath: selectedIndexPath, hideProgress: true, hideImage: false, cell: cell)
                            self.inProgress -= 1
                            cell.isUserInteractionEnabled = true

                        }
                    })
                }
            // track the progress of the data
            }, progressBlock: { (percent) in
                self.activityIndicatorView.stopAnimating()
                cell.progessBar.isHidden = false
                //cell.progessBar.transform = cell.progessBar.transform.scaledBy(x: 1, y: 1.1)
                cell.contentView.bringSubview(toFront: cell.progessBar)
                cell.progessBar.setProgress(Float(percent) / Float(100), animated: true)
                cell.isUserInteractionEnabled = false
            })
        }
    }
}

func removeDataFromDevice(cell: JourneyCollectionViewCell, selectedIndexPath: IndexPath, object: PFObject) {

        let selectedPartName = self.partArray[selectedIndexPath.item].name

        // unpin the object from the LocalDataStore
        PFObject.unpinAll(inBackground: [object], block: { (success, error) in
            if success {
                // reduce inProgress
                self.inProgress -= 1
                self.reloadCell(selectedIndexPath: selectedIndexPath, hideProgress: true, hideImage: true, cell: cell)
            }
        })
}

and this is how I'm reloading the cell

func reloadCell(selectedIndexPath: IndexPath, hideProgress: Bool, hideImage: Bool, cell: JourneyCollectionViewCell) {
    cell.progessBar.isHidden = hideProgress
    cell.imageDownloaded.isHidden = hideImage
    self.collectionView.reloadItems(at: [selectedIndexPath])
}

----------- EDIT -------------

This is my cellForItem at function. Presently i am using a query to look on local drive and see if the file exists and then adding the corner image if it is. This is the first time i have used a query in this place, usually it is a query at login to populate an array but that is for a more static collection of data than what i am trying to achieve here by letting the user download and delete files.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let cell: JourneyCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! JourneyCollectionViewCell

        cell.imageCell.file = self.partArray[indexPath.item].image
        cell.imageCell.loadInBackground()
        cell.imageCell.layer.masksToBounds = true

    // not sure if its good to run a query here as its constantly updated.
    // query if file is on LDS and add image to indicate
    let cellPartName = self.partArray[indexPath.item].name
    let checkQuery = PFQuery(className: "downloadedAudio")
        checkQuery.whereKeyExists(cellPartName)
        checkQuery.fromLocalDatastore()
        checkQuery.getFirstObjectInBackground(block: { (object, error) in
        if error != nil || object == nil {
            //print("The file does not exist locally on the device, remove the image.")
            cell.imageDownloaded.isHidden = true
            cell.imageDownloaded.image = UIImage(named: "")
            cell.progessBar.isHidden = true

        } else {
            //print("the file already exists on the device, add the image.")
            cell.contentView.bringSubview(toFront: cell.imageDownloaded)
            cell.imageDownloaded.isHidden = false
            cell.imageDownloaded.image = UIImage(named: "download-1")

            }
        })
    return cell
} 
Pippo
  • 1,439
  • 1
  • 18
  • 35

2 Answers2

3

This is a normal feature of "reuse" cells, for efficient memory management purposes. What you need to do is reset the cell values in below function:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
}

By reset, I mean set the cells to their default state, prior to you making any updates such as adding the left corner icon or the status bar.

You need to make sure the arrays that you are feeding the collectionview data from is maintained properly. For example, if you have an array A =[1,2,3] and you delete A[1], then array A needs to be [1,3].

DevKyle
  • 1,091
  • 6
  • 22
  • I am slightly unsure what you mean exactly, by reset the cells to their default state before adding any extra ui elements. I have attached my cellForItemAt function to the original question... So what its doing is using an array built at login to populate the main image (green 1 - 2 ect) and some other data used elsewhere. Then (and this is the only place i have done this before) its querying the local device for the file name (which is from the aforementioned array) to see if it exists... – Pippo Jan 29 '17 at 04:32
  • The functions in the main question (downloadDataToDevice and removeDataFromDevice) either add or remove this data from the local device and local data table so i think this handles the maintenance part you mentioned. I suppose adding the ui elements programatically just to the required cell when needed may be a more accurate approach. – Pippo Jan 29 '17 at 04:44
1

So i tried placing the progress view programatically, i tried prepareForReuse in the custom cell class, neither resolved this issue directly, though i will keep using prepareForReuse as i think its a cleaner way to manage the cell than i had been.

What seems to have worked was relocating the cell within the progressBlock

if let downloadingCell = self.collectionView.cellForItem(at: selectedIndexPath) as? JourneyCollectionViewCell {                            downloadingCell.progessBar.isHidden = false
    downloadingCell.contentView.bringSubview(toFront: downloadingCell.progessBar)
    downloadingCell.progessBar.setProgress(Float(percent) / Float(100), animated: true)
    downloadingCell.setNeedsDisplay()
    downloadingCell.isUserInteractionEnabled = false
}

enter image description here

Pippo
  • 1,439
  • 1
  • 18
  • 35