【问题标题】:Why is UICollectionView reloadData crashing my app?为什么 UICollectionView reloadData 会使我的应用程序崩溃?
【发布时间】:2015-08-13 22:22:42
【问题描述】:

我正在使用 uicollectionview 显示一些照片,并且我有一个允许用户删除所选照片的​​按钮。

除非我尝试删除用于填充 uicollectionview 的照片数组中的最后一张照片,否则它工作得很好。即,如果有 5 张照片,那么如果用户删除了第 5 张照片而不是第 1、2、3 或 4 张照片,则会出现问题。当我尝试删除第五张照片时,它在 reloadData() 上崩溃并出现以下错误

由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“尝试从第 0 节中删除第 4 项,它在更新前仅包含 4 个项目”

我不明白为什么会发生这种情况......该错误甚至提到“尝试删除”,但我从未真正告诉它我正在删除任何内容。我只是更改了源,然后要求它更新。同样在错误消息中,它说该部分仅包含更新前的 4 个项目,而它们实际上是 5 张照片。为什么?

关于我在做什么以及如何(使用 Swift)的更多信息...

我有一个 ProgressPhotoSheetClass,我从中实例化了对象 progressPhotoSheet

这个 progressPhotoSheet 对象有一个照片数组,照片可以有优先级,例如照片 ID、标题等

在我使用的部分中我的项目数

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

在我使用的 cellForItemAtIndexPath 中

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

...等等..

删除我使用的项目时

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

从设备和我的核心数据中删除照片,然后使用修改后的核心数据重新加载progressPhotoSheet.progressPhotos,因此它现在完全是以前的样子,但没有删除的照片。

然后我打电话

self.collectionView.reloadData()

应该为新数据更新 UICollectionView

如果我使用的是集合视图中的内容与数据源中的内容之间是否存在不匹配,我可以理解 self.collectionView.deleteItemsAtIndexPaths(indexPaths) 因为那会说忽略以使它们匹配我们需要删除一个项目 - 这里有可能有些东西可能不匹配.. 但是肯定使用 self.collectionView.reloadData() 进行什么更改并不重要它应该只是看看现在有什么数据并相应地更新 UICollectionView....

所以我的问题是......为什么我会收到这个错误,我应该怎么做才能解决问题,所以我不明白?

编辑以包含更多信息

这是我的长焦代码

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() 然后在从核心数据中获取新数据之前清空 progressPhotoSheet.progressPhotos... 下面的代码...

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)
        }

    }
}

如您所见,此时照片实际上并未被删除,我只是将 toDelete 标志设置为 true,然后仅重新加载 toDelete 设置为 false 的项目。这些照片稍后会根据网络连接等异步删除,因为它们也存储在服务器上供主网站使用。

【问题讨论】:

  • 请格式化您的问题/代码,使其更具可读性
  • 请发布您的 deleteItems 代码。你在使用 BeginUpdates 和 EndUpdates 吗?您是否也在重新加载 CollectionView 之前重新初始化 progressPhotoSheet.progressPhotos。
  • 您好,对格式问题感到抱歉。我在代码等前面放了 4 个空格,但格式没有显示出来。如果我去编辑它,空间就在那里!我不知道我还能做些什么来正确格式化它。有什么想法吗?
  • 修复了一些格式...并且我添加了 deleteItems 代码等。我没有使用 BeginUpdates 或 End Updates.. 事实上我没有听说过它们。它们是什么以及它们如何提供帮助?谢谢@ArunGupta
  • 什么时候打电话给self.collectionView.reloadData()

标签: ios iphone swift uicollectionview reloaddata


【解决方案1】:

您是否尝试过在 collectionView 上调用 invalidateLayout()?如果您的视图为空,即存在 0 个元素,这可能会有所帮助。

【讨论】:

    猜你喜欢
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多