从 UICollectionViewController 删除单元格的问题

Posted

技术标签:

【中文标题】从 UICollectionViewController 删除单元格的问题【英文标题】:Issue deleting cells from UICollectionViewController 【发布时间】:2018-06-01 19:00:28 【问题描述】:

我正在使用 UICollectionViewController,我正在尝试在所选项目的循环中使用 deleteItems,如下所示:

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

是对删除单元格内的图像并将其从数据库中删除的 API 的调用。

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)

            

        

    

这是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)
                

        
    

我的循环问题是有时它可以工作,有时我会收到这个错误:

无效更新:第 0 节中的项目数无效。 更新 (1) 后包含在现有部分中的项目必须是 等于该部分中包含的项目数之前 update (1),加上或减去插入或删除的项目数 该部分(0 插入,1 删除)和加或减的数量 移入或移出该部分的项目(0 移入,0 移出)。

有时我会收到此错误:

线程 1:致命错误:索引超出范围

在这条线上

self.array.remove(at: item.item)

我做错了什么?

更新

我从一个答案中得到了这个:

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

        

    

但有时我会在这一行出现错误:

let cell = self.collectionView?.cellForItem(at: item) as! ImageCollectionCell

这是错误:

线程 1:致命错误:打开包装时意外发现 nil 可选值

我注意到这只发生在 iPhone 上,如果我选择图像并滚动 UICollectionView,然后单击删除按钮。

更新

我也试过这个:

@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()

                    
                
            
        

    

但我收到此错误:

线程 1:致命错误:索引超出范围

在这一行:

self.array.remove(at: item.item)

【问题讨论】:

评论您的更新:self.array.remove(at: item.item) 正在为您崩溃,所以您能否在执行删除操作之前打印您的数组,并在其下方打印 item.item,您收到错误,因为 item 是您正在尝试删除的不是您的数组中的退出(数组的元素较少) 【参考方案1】:

有两个问题:

    在使用collectionView?.indexPathsForSelectedItems的循环中更新collectionView 打电话给collectionView?.deleteItems(at: [item]) 太频繁了

最好的选择是对所有索引执行网络请求,然后一步将它们从 collectionView 中删除。您可以将请求与DispatchGroup 合并到一个链中。

一些解释:删除两个项目。 1 和 2。有时会先删除 1,然后 indexPathsForSelectedItems 将只包含一个索引 (1)。但是循环会尝试删除 indexPath 2 处的项目。这是一个崩溃。

在您的代码中使用 DispatchGroup 的示例:

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)

【讨论】:

我可以使用我的代码获取 DispatchGroup 的示例吗? 这里出现错误:let cell = self.collectionView?.cellForItem(at: item) as! ImageCollectionCell 线程 1:致命错误:在展开可选值时意外发现 nil 如果删除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) 会怎样? 我现在试试【参考方案2】:

如果你得到错误

部分中的项目数无效

因为,随着任何集合的变化,它希望拥有与源数组相同数量的项目,您可以使用 performBatchUpdates 来解决这个问题:

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

            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"

            
        
    

【讨论】:

我想把这段代码放在哪里?替换我的删除按钮中的所有内容? 您的代码出现此错误:条件绑定的初始化程序必须具有可选类型,而不是此行上的“ImageCollectionCell”:if let cell = self.collectionView?.cellForItem(at: item) as! ImageCollectionCell 我修复了这个:可选类型,而不是 'ImageCollectionCell' 如果让 cell = self.collectionView?.cellForItem(at: item) as!图像收集单元? 但是当我运行代码时出现新错误:线程 1:致命错误:此行的索引超出范围 self.array.remove(at: item.item) 仍然遇到同样的错误:线程 1:致命错误:self.array.remove(at:item.item) 上的索引超出范围【参考方案3】:

正如上面所有答案所说 - 您需要使用 performBatchUpdates 方法进行 UICollectionView 更新。

来自文档:

您可以在想要制作多个的情况下使用此方法 在一个动画操作中更改集合视图,如 反对在几个单独的动画中。你可以使用这个方法 插入、删除、重新加载或移动单元格或使用它来更改 与一个或多个单元格相关的布局参数。使用块 传入更新参数以指定您的所有操作 想表演。

这只是意味着您需要在performBatchUpdates闭包内一次性删除所有项目。

您可以使用此代码来执行此操作:

var indexes: [IndexPath] = []
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?.deselectItem(at: item, animated: true)
            indexes.append(item.item)
        
    

indexes.forEach  self.array.remove(at: $0) 
collectionView?.performBatchUpdates(
    self.collectionView?.deleteItems(at: [indexes])
)  completed in
    self.navigationItem.rightBarButtonItems = [self.editButton]
    self.editButton.isEnabled = true
    self.editButton.title = "Edit"
    self.doneButton.title = "Done"

【讨论】:

【参考方案4】:

我认为您应该首先从数组中删除项目而不是更新数据库我的项目有类似的问题,这个简单的更改解决了这个问题。我回家后尝试发布一些代码。希望这会有所帮助。

【讨论】:

以上是关于从 UICollectionViewController 删除单元格的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何从其他面板从 JTextField 获取输入

从PRISM开始学WPFMVVMViewModel?

在 python 中,为啥从数组读取比从列表读取慢?

从图库中挑选或从相机捕获的高质量图像

从PRISM开始学WPFMVVMCommand?

从PRISM开始学WPFPrism?