2

I am using uicollectionview to display some photos and I have a button that allows the user to delete the selected photo.

It works perfectly unless I try to delete the last photo in the array of photos being used to populate the uicollectionview. Ie if there are 5 photos then there will be a problem if a user removes the 5th photo but not the 1st, 2nd, 3rd or 4th. When I try to delete the 5th photo it crashes on reloadData() with the following error

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete item 4 from section 0 which only contains 4 items before the update'

I don't understand why this would happen... The error even mentions "attempt to delete" but I never actually told it I was deleting anything. I just changed the source and then asked it to update. Also in the error message it says the section only contains 4 items before the update when their were actually 5 photos. Why?

A little bit more info about what I'm doing and how (Using Swift)...

I've got a ProgressPhotoSheetClass from which I have instantiated the object progressPhotoSheet

this progressPhotoSheet object has an array of photos and the photos can have priorities such as photo id, title etc

in my number of items in section I use

var numPics: Int = progressPhotoSheet.progressPhotos.count
return numPics

in my cellForItemAtIndexPath I use

let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! PhotoHolder
cell.photoTitle.text = progressPhotoSheet.progressPhotos[indexPath.row].photoName

...etc..

When deleting the item I use

progressPhotoSheet.deletePhoto(progressPhotoSheet.progressPhotos[indexPath.row].photoId)

which deletes the photo from the device and from my core data and then reloads progressPhotoSheet.progressPhotos from using the modified core data so it is now exactly how it was before but without the deleted photo.

I then call

self.collectionView.reloadData()

Which should update the UICollectionView for the new data

I could understand if it felt there was a mismatch between what should be in the collection view and what is in the datasource if I were using self.collectionView.deleteItemsAtIndexPaths(indexPaths) because that would be saying ignored to get them to match we need to delete one item - here there is a possibility something could mismatch.. But surely using self.collectionView.reloadData() it doesn't matter what changes were made it should just look at what data is there now and update the UICollectionView accordingly....

So my question is... Why am I getting this error and what should I do to fix things so I don't get it?

Edit to include more info

Here is my telephoto Code

func deletePhoto(photoId: Int) {

    // Set up Core Data Managed Object Context
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!



    // Fetch correct photo
    let fetchRequest = NSFetchRequest(entityName: "CDProgressPhoto")
    fetchRequest.predicate = NSPredicate(format: "photoId = %@", String(photoId))

    // Save
    if let fetchResults = managedContext.executeFetchRequest(fetchRequest, error: nil) as? [NSManagedObject] {
       if fetchResults.count != 0{

            // Will only be one photo with this photo id
            var photo = fetchResults[0]

            photo.setValue(true, forKey: "toDelete")

            // Save the object
            var error: NSError?
            if !managedContext.save(&error) {
                println("Could not save \(error), \(error?.userInfo)")
            }
        }
    }



    // Reload from core data
    self.loadPhotoSheetFromCoreData()
}

self.loadPhotoSheetFromCoreData() then empties progressPhotoSheet.progressPhotos before getting the new data from core data... Code below...

private func loadPhotoSheetFromCoreData() {

    if(self.hasPhotoSheet()) {

        // Clear existing photos
        self.progressPhotos = []

        // Set up Core Data Managed Object Context
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedContext = appDelegate.managedObjectContext!

        let request = NSFetchRequest(entityName: "CDProgressPhoto")
        let predicate1 = NSPredicate(format: "photoSheetId == %@", String(self.sheetId))
        let predicate2 = NSPredicate(format: "toDelete == %@", false)
        request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false) as NSSortDescriptor]
        var predicatesArray: [NSPredicate] = [predicate1, predicate2]
        //predicatesArray.append(predicate1)
        request.predicate = NSCompoundPredicate.andPredicateWithSubpredicates(predicatesArray)
        let existings = managedContext.executeFetchRequest(request, error: nil)
        let existingPhotos: [CDProgressPhoto] =  existings as! [CDProgressPhoto]

        // for each photo make a ProgressPhoto object and add to progress photos array
        for photo in existingPhotos {
            var newPhoto: ProgressPhoto = ProgressPhoto()
            newPhoto.photoSheetId = Int(photo.photoSheetId)
            newPhoto.photoId  = Int(photo.photoId)
            newPhoto.photoName  = photo.photoName
            newPhoto.date = Int(photo.date)
            newPhoto.url = photo.url
            newPhoto.filename = photo.filename
            newPhoto.height = Float(photo.height)
            newPhoto.width = Float(photo.width)
            newPhoto.selected = false

            self.progressPhotos.append(newPhoto)
        }

    }
}

As you can see the photo isn't actually deleted at this point I just set a toDelete flag to true and then only re load items where toDelete is set to false. The photos are deleted later asynchronously depending on network connection etc because they are also stored on a server for use on the main website.

365SplendidSuns
  • 3,175
  • 1
  • 21
  • 28
  • please format your question / code so it's more readable – Wain May 31 '15 at 11:40
  • Please post your deleteItems code. Are you using BeginUpdates and EndUpdates? Also are you reinitialising progressPhotoSheet.progressPhotos before reloading CollectionView. – Arun Gupta May 31 '15 at 11:43
  • Hi, sorry about the formatting. I put 4 spaces in front of the code etc but the formatting isn't showing up. If I go to edit it the spaces are there! I don't know what more I can do to format it correctly. Any ideas? – 365SplendidSuns May 31 '15 at 11:47
  • Fixed some formatting... And I've added the deleteItems code etc. I'm not using BeginUpdates or End Updates.. In fact I've not heard of them. What are they and how might they help? Thanks @ArunGupta – 365SplendidSuns May 31 '15 at 12:00
  • When do you call `self.collectionView.reloadData()`? – Bannings May 31 '15 at 13:51
  • One correction BeginUpdates is for UITableView only. Also did you check the array after setting the delete flag. The count after should be one less. – Arun Gupta May 31 '15 at 15:40
  • @Bannings I call self.collectionView.reloadData() immediately after callingprogressPhotoSheet.deletePhoto() – 365SplendidSuns May 31 '15 at 15:59
  • @ArunGupta Yep, the count after is one less. As an experiment I tried changing my numberOfItemsInSection function to show the number of items in the collection view rather than the number of items in the data source (i.e. the number of items is reduced when reloadData is called rather than immediately before it) This got rid of the current error but caused other problems and I don't think is the right direction to be going. – 365SplendidSuns May 31 '15 at 16:05
  • @ArunGupta Surely it doesn't matter if it is one less anyway. Say there was a mistake deleting the item from the array so it was exactly the same after as before surely calling reload should then just load it how it was before? – 365SplendidSuns May 31 '15 at 16:07
  • Let check what happens when on delete you remove the item from the progressPhotos array and reloads the collectionView – Arun Gupta May 31 '15 at 18:24
  • Can you share your project on GitHub? – Bannings Jun 01 '15 at 03:44
  • @ArunGupta Do you mean move the reloadData call to inside the deletePhoto function? – 365SplendidSuns Jun 01 '15 at 08:20
  • @Bannings The whole app / project does a lot more than just this and I'd rather not publicly share it all – 365SplendidSuns Jun 01 '15 at 08:21

1 Answers1

-1

Have you tried calling invalidateLayout() on the collectionView? That might help incase your view is empty i.e. 0 elements are present.

InherentlyNuts
  • 199
  • 2
  • 8