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.