【问题标题】:Using a button to deselect cells in a collectionview使用按钮取消选择集合视图中的单元格
【发布时间】:2017-03-29 17:06:18
【问题描述】:

我有两个集合视图。点击集合视图 1 中未选择的单元格将选中它们,并将选定的单元格添加到集合视图 2。点击集合视图 1 (allHobbiesCV) 中的选定单元格将取消选择它们并将它们从集合视图 2 (myHobbiesCV) 中删除。本质上,它所做的只是切换。

Collection View 2 中的单元格也可以手动删除,方法是根据需要选择少或多,然后按“删除”按钮。这个过程效果很好,除了集合视图 1 中的单元格仍然保持选中状态,即使该特定单元格已从集合视图 2 中删除。

如果手动选择并从 Collection View 2 中删除单元格,如何使用删除按钮从 Collection View 1 中取消选择?

类级别 -

var allHobbiesArray = [String]()
var allHobbiesArraySelected = [String]()

var myHobbiesArray = [String]()
var myHobbiesArraySelected = [String]()

didSelectItemAt

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {


    // All Hobbies
    if collectionView == allHobbiesCV {
        let item = allHobbiesArray[indexPath.item]
        let cell = allHobbiesCV.cellForItem(at: indexPath) as! AllHobbiesCell

        if let itemIndex = allHobbiesArraySelected.index(of:item) {
            // DID DESELECT
            allHobbiesArraySelected.remove(at:itemIndex)
            myHobbiesArray.remove(at: itemIndex)
            myHobbiesCV.deleteItems(at: [IndexPath(item: itemIndex, section:0)])
            cell.backgroundColor = UIColor.brown
        }
        else {
            // DID SELECT
            allHobbiesArraySelected.insert(item, at: 0)
            myHobbiesArray.insert(item, at: 0)
            myHobbiesCV.insertItems(at: [IndexPath(item: 0, section:0)])
            cell.backgroundColor = UIColor.green
        }

        allHobbiesCV.deselectItem(at: indexPath, animated: false)

        for cell in myHobbiesCV.visibleCells {
            cell.backgroundColor = UIColor.white
        }
        myHobbiesArraySelected.removeAll()
        //myHobbiesCV.reloadData() // needed?
    }

    // My Hobbies
    else {
        let item = myHobbiesArray[indexPath.item]
        let cell = myHobbiesCV.cellForItem(at: indexPath) as! MyHobbiesCell

        if let itemIndex = myHobbiesArraySelected.index(of:item) {
            // DID DESELECT
            myHobbiesArraySelected.remove(at: itemIndex)
            cell.backgroundColor = UIColor.white
        }
        else {
            // DID SELECT
            myHobbiesArraySelected.insert(item, at: 0)
            cell.backgroundColor = UIColor.red
        }
        myHobbiesCV.deselectItem(at: indexPath, animated: true)
    }
}

cellForItem

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    if collectionView == allHobbiesCV {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ALL", for: indexPath) as! AllHobbiesCell
        cell.allHobbiesCellLabel.text = allHobbiesArray[indexPath.item]

        let allHobbies = allHobbiesArray[indexPath.item]
        if allHobbiesArraySelected.index(of: allHobbies) != nil {
            cell.backgroundColor = UIColor.green
        }
        else {
            cell.backgroundColor = UIColor.brown
        }

        return cell
    }

    else { // myHobbiesCV
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MY", for: indexPath) as! MyHobbiesCell
        cell.myHobbiesCellLabel.text = myHobbiesArray[indexPath.item]

        let myHobbies = myHobbiesArray[indexPath.item]
        if myHobbiesArraySelected.index(of: myHobbies) != nil {
            cell.backgroundColor = UIColor.red
        }
        else {
            cell.backgroundColor = UIColor.white
        }


        return cell
    }

}

numberOfItemsInSection

    if collectionView == allHobbiesCV {
        return allHobbiesArray.count
    }
    else {
        return myHobbiesArray.count
    }

删除按钮

@IBAction func deleteHobbyButtonPressed(_ sender: UIButton) {

    print("all Hobbies - \(allHobbiesCV.indexPathsForSelectedItems!)")

    if let selectedItemPaths = myHobbiesCV.indexPathsForSelectedItems {

        var allItemIndexPaths = [IndexPath]()
        var tempSelectedItems = Array(allHobbiesArraySelected) // Need to use a temporary copy otherwise the element indexes will change
        for itemPath in selectedItemPaths {

            let removeItem = allHobbiesArraySelected[itemPath.item]
            if let removeIndex = tempSelectedItems.index(of: removeItem) {
                print("if let removeIndex")
                tempSelectedItems.remove(at: removeIndex)
            }

            if let allItemsIndex = allHobbiesArray.index(of: removeItem) {
                print("if let allItemsIndex")
                allItemIndexPaths.append(IndexPath(item: allItemsIndex, section: 0))
            }
        }

        allHobbiesArraySelected = tempSelectedItems // Selected items array without the removed items
        myHobbiesCV.deleteItems(at:selectedItemPaths)
        myHobbiesCV.reloadData()
        allHobbiesCV.reloadItems(at: allItemIndexPaths)  // Reload to update the selected status
    }

}

现在的问题是删除按钮中没有任何评估。第一个打印语句总是返回一个空数组。 if let 检查中没有打印任何内容。有没有办法使用 myHobbiesArraySelected 而不是 indexPathsForSelectedItems? (因为我现在将所选项目保存在数组中)以及取消选择 allHobbiesCV 中的单元格的原始预期功能,如果它是在 myHobbiesCV 中手动删除的。

感谢朋友。

【问题讨论】:

    标签: ios arrays swift uicollectionview


    【解决方案1】:

    存储选定的索引路径是个坏主意,因为它们与集合视图耦合。如果您存储选定的items,那么您始终可以使用数组中的项目索引来确定适当的索引路径。您还可以从给定的索引路径快速确定一个项目。

    在下面的代码中,我使用Item 类型作为您的基础项目,但它可以是StringInt 或任何对象类型。如果您使用自己的类或结构,请确保使其符合 EquatableHashable

    var allItems:[Item]
    var selectedItems[Item]
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if collectionView == allItemsCV {
            let item = allItems[IndexPath.row]
            if let itemIndex = selectedItems.index(of:item) {
                selectedItems.remove(at:itemIndex)
                selectedItemsCV.deleteItems(at: [IndexPath(item: itemIndex, section:0)])
            } else {
                selectedItems.insert(item, at: 0)
                selectedItemsCV.insertItems(at: [IndexPath(item: 0, section:0)])
            }
            allItemsCV.deselectItem(at: indexPath, animated: true)
           // cell for item at indexPath needs to render the selected/deselected 
              state correctly based on the item being in the selectedItems array
    
            allItemsCV.reloadItems(at: [indexPath])
    
        }
    }
    
    @IBAction func removeButtonPressed(_ sender: UIButton) {
    //...
        if let selectedItemPaths = selectedItemCV.indexPathsForSelectedItems {
            var allItemIndexPaths = [IndexPath]()
            var tempSelectedItems = Array(selectedItems) // Need to use a temporary copy otherwise the element indexes will change
            for itemPath in selectedItemPaths {
                let removeItem = selectedItems[itemPath.item]
                if let removeIndex = tempSelectedItems.index(of: removeItem) {
                    tempSelectedItems.remove(at: removeItem)
                }
                if let allItemsIndex = allItems.index(of: removeItem) {
                    allItemIndexPaths.append(IndexPath(item: allItemsIndex, section: 0))
                }
            }
            selectedItems = tempSelectedItems // Selected items array without the removed items
            selectedItemsCV.deleteItems(at:selectedItemPaths)
            allItemsCV.reloadItems(at: allItemIndexPaths)  // Reload to update the selected status
        }
    }
    

    【讨论】:

    • 过去一个半小时的大部分时间都在研究你的代码。我赶上了 allItems / selectedItems。我希望这些是 Int,而不是 Item,这样我就可以获得正确的索引路径,是吗?我已经在原始帖子中更新了我的代码。在 didSelectItemAt 上崩溃(致命错误:索引超出范围)。 allItems 实际上是我的数据源吗? (这只是一个用于填充单元格的字符串数组)
    • 不,不要存储索引路径:它们会随着您插入/删除项目而改变。存储实际项目并在需要时在数组中找到它。
    • 好吧,我又修正了我的代码。现在不存储索引路径;存储项目。单元格选择/取消选择正在工作(尽管所选状态滑入的奇怪视觉问题。)我想我可以解决这些问题......但是在deleteButtonPressed上发生了崩溃。 *** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的项目数无效。***。我在这里迈向正确的方向吗?
    • 此外,在 didDeselect 中不再需要任何逻辑,对吗? (除了视觉上的变化)
    • 是的。这意味着numberOfItemsInSection 返回的项目数与集合视图认为 应该存在的项目数不匹配;例如如果有 3 个并且您删除了 1,那么应该有 2 个。如果 numberOfItems 返回一个不同的数字,那么您会得到该异常
    【解决方案2】:

    更新: IndexPath 符合 Equatable(参见 @Paulw11 的评论) 您比较 contains 中的对象 IndexPath 但即使 indexPath 路径的实例具有相同的部分和行,对象本身也是不同的。

    1.解决方案:比较值 更改包含以比较部分和行

    1. 解决方案:坚持项目ID 坚持存储在数组中的唯一项目 ID。如果collection1 和collection2 有不同的大小和部分,那么您将处于更节省的情况

    我建议实施解决方案 2

    【讨论】:

    • #2 听起来像是一个计划。您将如何将唯一 id 关联到数组元素?
    • @muescha, NSIndexPath 符合 EquatableHashable。这确保了诸如contains 之类的操作将正常工作; contains 不仅比较对象引用,它还通过Equatable 协议比较索引路径值,以便具有相同部分和行的两个“不同”IndexPath 相等。
    猜你喜欢
    • 2016-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多