I am using UICollectionViewController and I am trying to use deleteItems in a loop of selected items like so:
for item in (collectionView?.indexPathsForSelectedItems)!
        {
            let cell = self.collectionView?.cellForItem(at: item) as! ImageCollectionCell
            deleteLandGradingImage(images: ImagesData(jobNo: self.jobNo, ImageBytes: (self.array[item.item]["imageBytes"] as! String), Username: appDelegate.username)) { result in
                cell.imageView.layer.borderWidth = 0
                self.navigationItem.rightBarButtonItems = [self.editButton]
                self.editButton.isEnabled = true
                self.editButton.title = "Edit"
                self.doneButton.title = "Done"
                self.array.remove(at: item.item)
                self.collectionView?.deselectItem(at: item, animated: true)
                self.collectionView?.deleteItems(at: [item])
            }
        }
deleteLandGradingImage(images: ImagesData(jobNo: self.jobNo, ImageBytes: (self.array[item.item]["imageBytes"] as! String), Username: appDelegate.username)) { result in
}
Is a call to an API that deletes the image inside the cell and removes it from the database.
func deleteLandGradingImage(images: ImagesData, completionHandler:@escaping (_ result:Bool) -> Void) {
        //Define array for returning data
        var returnedResults = Bool()
        //Call API
        WebService().deleteLandGradingImages(images: images)
        {
            (result: Bool) in
            DispatchQueue.main.async {
                //Return our results
                returnedResults = result
                completionHandler(returnedResults)
            }
        }
    }
and here is the method in WebService:
func deleteLandGradingImages(images: ImagesData, completion: @escaping (_ result: Bool) -> Void)
    {
        var jsonDict = [AnyHashable: Any]()
        //Set Lot
        jsonDict["jobNo"] = images.jobNo
        //Set Image
        jsonDict["imageBytes"] = images.ImageBytes
        let jsonData: Data? = try? JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted)
        let urlComponents = NSURLComponents(string: webservice + "DeleteGradingImages");
        //Assign Username
        urlComponents?.user = appDelegate.username;
        //Assign Password
        urlComponents?.password = appDelegate.password;
        //Define our URL String
        let url = urlComponents?.url;
        //Define URL Request
        var request = URLRequest(url: url!)
        //Set Header Values for request
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        //Set Header Values for request
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        //Set Request Method to POST
        request.httpMethod = "POST"
        //Set Request Body to JSON Data
        request.httpBody = jsonData
        Alamofire.request(request)
            .responseJSON { response in
                if(response.error != nil)
                {
                    completion(false)
                }
                else
                {
                    let responseString = (String(data: response.data!, encoding: .utf8) != nil) as Bool
                    completion(responseString)
                }
        }
    }
My problem with the loop is that sometimes it works, sometimes I get this error:
Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (1) must be equal to the number of items contained in that section before the update (1), plus or minus the number of items inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).
Sometimes I get this error:
Thread 1: Fatal error: Index out of range
on this line
self.array.remove(at: item.item)
What am I doing wrong?
UPDATE
I got this from an answer:
@IBAction func deleteButtonPressed(_ sender: Any) {
        let group = DispatchGroup()
        let indexPaths = (collectionView?.indexPathsForSelectedItems)!
        var deletes = Array<Dictionary<String, Any>>()
        for item in indexPaths {
            deletes.append(self.array[item.item])
        }
        for item in deletes {
            group.enter()
            deleteLandGradingImage(images: ImagesData(jobNo: self.jobNo, ImageBytes: (item["imageBytes"] as! String), Username: appDelegate.username)) { result in
                let i = self.array.index { (dic) -> Bool in
                    return true
                }
                if i != nil {
                    self.array.remove(at: i!)
                    group.leave()
                }
            }
        }
        group.notify(queue: .main) {
            for item in indexPaths {
                let cell = self.collectionView?.cellForItem(at: item) as! ImageCollectionCell
                cell.imageView.layer.borderWidth = 0
                self.collectionView?.deselectItem(at: item, animated: true)
            }
            self.navigationItem.rightBarButtonItems = [self.editButton]
            self.editButton.isEnabled = true
            self.editButton.title = "Edit"
            self.doneButton.title = "Done"
            self.collectionView?.deleteItems(at: indexPaths)
            if(self.array.count == 0)
            {
                self.imageCollectionDelegate?.ImageCollectionChange(false)
            }
        }
    }
But sometimes I get an error on this line:
let cell = self.collectionView?.cellForItem(at: item) as! ImageCollectionCell
This is the error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
I noticed this only happens on iPhone and if I select images and scroll through the UICollectionView, then click the delete button.
UPDATE
I have also tried this:
@IBAction func deleteButtonPressed(_ sender: Any) {
        self.createIndicator()
        for item in (collectionView?.indexPathsForSelectedItems)! {
            if let cell = self.collectionView?.cellForItem(at: item) as! ImageCollectionCell? {
                deleteLandGradingImage(images: ImagesData(jobNo: self.jobNo, ImageBytes: (self.array[item.item]["imageBytes"] as! String), Username: appDelegate.username)) { result in
                    cell.imageView.layer.borderWidth = 0
                    self.collectionView?.performBatchUpdates({
                        self.array.remove(at: item.item)
                        self.collectionView?.deselectItem(at: item, animated: true)
                        self.collectionView?.deleteItems(at: [item])
                    }) { completed in
                        self.navigationItem.rightBarButtonItems = [self.editButton]
                        self.editButton.isEnabled = true
                        self.editButton.title = "Edit"
                        self.doneButton.title = "Done"
                        self.stopIndicator()
                    }
                }
            }
        }
    }
But I get this error:
Thread 1: Fatal error: Index out of range
On this line:
self.array.remove(at: item.item)
                Run Project. Build and Run the project and select the Edit Button. Select a few cells and press the Trash button to remove the items.
You can call this method at any time to update the layout information. This method invalidates the layout of the collection view itself and returns right away. Thus, you can call this method multiple times from the same block of code without triggering multiple layout updates.
An object that manages an ordered collection of data items and presents them using customizable layouts.
There are two problems:
collectionView?.indexPathsForSelectedItems
collectionView?.deleteItems(at: [item]) too oftenBest choice is to perform network requests for all indexes and then remove them from collectionView in one step. You can combine requests in one chain with DispatchGroup.
Some explanation: Removing two items. 1 and 2. Sometimes 1 will be removed first, and after that indexPathsForSelectedItems will contains just one index(1). But loop will try to remove item at indexPath 2. It's a crash.
Sample of using DispatchGroup with your code:
let group = DispatchGroup()
let indexPaths = (collectionView?.indexPathsForSelectedItems)!
var deletes = [<your type>]()
for item in indexPaths {
    deletes.append(array[item.item])
}
// TODO: Block UI with UIActivityIndicatorView
for item in deletes {
    group.enter()
    deleteLandGradingImage(images: ImagesData(jobNo: self.jobNo, ImageBytes: (item["imageBytes"] as! String), Username: appDelegate.username)) { result in
        let i = self.array.index(of: item)!
        self.array.remove(at: i)
        group.leave()
    }
}
group.notify(queue: .main) {
    self.navigationItem.rightBarButtonItems = [self.editButton]
    self.editButton.isEnabled = true
    self.editButton.title = "Edit"
    self.doneButton.title = "Done"
    self.collectionView?.deleteItems(at: indexPaths)
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With