UICollectionViewCell duplication

2019-07-29 07:22发布

问题:

I know this was asked here before already, but my issue is a bit different, and following this guide didn't help:

CollectionView duplicate cell when loading more data

So my situation is:

  1. I'm on ProductPageView - Deleting Product
  2. Product removed from Database (Successfully)
  3. Unwind Segue fires up
  4. In callback, I remove the deleted product from collection
  5. I refresh collection
  6. View loads up with duplicate cell

Beginning of my cellForItemAtIndexPath :

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "product_collection_cell", for: indexPath) as! ProductsCollectionViewCell
    cell.ProductImageView.image = nil
    cell.ProductName.text = nil
    cell.ProductPrice.text = nil
    cell.productUniqueID = nil

    let prodInCell =  searchActive ? filtered[indexPath.row] : products[indexPath.row]

    let prodID = prodInCell.getUniqueID()
    let dbRef = Storage.storage().reference().child(prodID).child("pic0.jpg")
    cell.contentMode = .scaleAspectFit
    cell.ProductImageView.image = #imageLiteral(resourceName: "DefaultProductImage")
    dbRef.downloadURL(completion:
        {
            url, error in
            if let error = error
            {
                print (error)
            }
            else if let url = url
            {
                cell.ProductImageView.loadImageUsingUrlString(urlString: url.absoluteString)
                cell.ProductImageView.contentMode = UIViewContentMode.scaleToFill
                cell.layoutIfNeeded()
            }
    })
    cell.ProductImageView.clipsToBounds = true

    //cell.ProductName = UILabel()
    cell.ProductName.text = prodInCell.getName()

    //cell.ProductPrice = UILabel()
    cell.ProductPrice.text = String(prodInCell.getPrice())
    cell.productUniqueID = prodInCell.getUniqueID()
    return cell
}

My unwind callback function:

@IBAction func unwindFromDeleteSegue(segue: UIStoryboardSegue)
{
    if (prodToLoad == nil) {return}

    for product in products
    {
        if product.getUniqueID() == prodToLoad?.getUniqueID()
        {
            products.remove(at: products.index(of: product)!)
            ProductsCollection.reloadData()
            break
        }
    }
}

My numberOfItemsInSection :

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        if searchActive
        {
            return filtered.count
        }
        else
        {
            return products.count    //return number of rows in section
        }
    }

Can anyone please point me out to where the source of my bug might be ?

Notice:

This doesn't occur only when loading more data, it also occurs when there is only 1 item in collection, and cells are not being reused

EDIT:

I noticed that when this line is in comment:

StaticFunctions.ProductsStaticFunctions.removeProductFromDatabase(productUniqueID: self.productToDisplay.getUniqueID(), ownerID: self.productToDisplay.getOwnerID())

It works fine. When I un-comment it, it stops working properly again.

public static func removeProductFromDatabase(productUniqueID: String, ownerID: String)
        {

        let childUpdates = ["/\(Constants.TREE_A)/\(ownerID)/\(productUniqueID)/" : nil,
                            "/\(Constants.TREE_B)/\(ownerID)/\(productUniqueID)/" : nil,
                            "/\(Constants.TREE_C)/\(productUniqueID)/" : nil,
                            "/\(Constants.TREE_D)/\(productUniqueID)/" : nil,
                            "/\(Constants.TREE_E)/\(ownerID)/\(productUniqueID)/": nil,
                            "/\(Constants.TREE_F)/\(ownerID)/\(productUniqueID)/": nil,
                            "/\(Constants.TREE_G)/\(productUniqueID)/" : nil,
                            "/\(Constants.TREE_H/\(productUniqueID)/" : nil
            ] as [String : Any?]

        Constants.refs.databaseRoot.updateChildValues(childUpdates)
    }

My method for adding product to array:

ref?.observe(.value, with:
            { (snapshot) in

            let allChildrenObjects = snapshot.children.allObjects
            if allChildrenObjects.count == 0 {self.StopLoadingAnimation() ; return}

            for child in allChildrenObjects as! [DataSnapshot]
            {
                // Code to execute when new product is added
                let prodValueDictionary = child.value as? NSDictionary

                if ((currentUserId == prodValueDictionary?[Constants.Products.ProductFields.PRODUCT_OWNER_ID] as? String) == itemsOfCurrentUser)
                {
                    self.AddProductToCollection(productAsDictionary: prodValueDictionary, IsProductMine: itemsOfCurrentUser)
                    self.DispatchQueueFunc()
                }
            }

回答1:

Since you use

ref?.observe(.value, with:
        { (snapshot) in

you'll be in threat that this callback is called every edit that occurs to it , which leads to an unexpected results as you encounter like duplicating data inside the array , so you need a single observe

ref?.observeSingleEvent(.value, with:
        { (snapshot) in