简体   繁体   中英

Swift - Toggling CollectionView cells to toggle items in an Array

I have two CollectionView's. One CollectionView (allHobbiesCV) is pre-populated with Hobbies you can select. The other CollectionView (myHobbiesCV) is empty, but if you tap on a hobby in the allHobbiesCV, it gets added to the myHobbiesCV. This is all working great.

I would like the tapped allHobbiesCV cells to switch to selected, it adds the hobby to myHobbiesCV, then if the user taps the same selected cell again in the allHobbiesCV, it removes that hobby from the myHobbiesCV. Basically a toggle add/remove.

Two things to note:

  1. Users can manually select hobbies in myHobbiesCV, then tap a [Remove Hobby] button.
  2. Hobbies will be sorted by seasons, so there will be 4 different data sets (Winter, Spring, Summer, Autumn) for allHobbiesArray. Depending on which season (ViewController) the user taps. They can select as many / few cells from each as they like.

Problem:

I'm crashing on any toggle besides the first cell. If I select the first cell in allHobbiesCV, I can select it again, and it will remove it from the myHobbiesCV. If I select that same cell again (to toggle it,) I crash. If I select any other cell besides the first, I crash.

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

Class Level

// Winter Hobbies
let allHobbiesArray = ["Skiing", "Snowboarding", "Drinking Bourbon", "Snow Shoeing", "Snowmobiling", "Sledding", "Shoveling Snow", "Ice Skating"]
var myHobbiesArray = [String]()
var allSelected = [IndexPath]()
var didSelectIPArray = [IndexPath]()

Data Source

extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

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


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

        if cell.isSelected {
            cell.backgroundColor = UIColor.green
        }
        else {
            cell.backgroundColor = UIColor.yellow
        }

        return cell
    }

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

        if cell.isSelected {
            cell.backgroundColor = UIColor.red
        }
        else {
            cell.backgroundColor = UIColor.yellow
        }


        return cell
    }


}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if collectionView == allHobbiesCV {
        return allHobbiesArray.count
    }
    else {
        return myHobbiesArray.count
    }

}

}

Delegate

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


    if collectionView == allHobbiesCV {
        if myHobbiesArray.count <= 6
            self.allSelected = []

            didSelectIPArray.append(indexPath) // Store the selected indexPath in didSelectIPArray

            myHobbiesArray.insert(allHobbiesArray[indexPath.item], at: 0)
            let allHobbiesCell = allHobbiesCV.cellForItem(at: indexPath) as! AllHobbiesCell
            allHobbiesCell.backgroundColor = UIColor.green

            // Store all of the selected cells in an array
            didSelectIPArray.append(indexPath) 

            myHobbiesCV.reloadData()
        }
    }


    else {
        let cell = myHobbiesCV.cellForItem(at: indexPath) as! MyHobbiesCell
        cell.backgroundColor = UIColor.red
        allSelected = self.myHobbiesCV.indexPathsForSelectedItems!
    }


}

// Deselecting selected cells
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {

    if collectionView == allHobbiesCV {

        // Yellow is the unselected cell color. So let's change it back to yellow.
        let allHobbiesCell = allHobbiesCV.cellForItem(at: indexPath) as! AllHobbiesCell
        allHobbiesCell.backgroundColor = UIColor.yellow

        // Remove (toggle) the cells. This is where I am stuck/crashing.
        let indices = didSelectIPArray.map{ $0.item }
        myHobbiesArray = myHobbiesArray.enumerated().flatMap { indices.contains($0.0) ? nil : $0.1 }
        self.myHobbiesCV.deleteItems(at: didSelectIPArray)

        myHobbiesCV.reloadData()

        didSelectIPArray.remove(at: indexPath.item) // Remove the deselected indexPath from didSelectIPArray
    }

    else { // MyHobbies CV
        let cell = myHobbiesCV.cellForItem(at: indexPath) as! MyHobbiesCell
        cell.backgroundColor = UIColor.yellow

        // Store the selected cells to be manually deleted.
        allSelected = self.myHobbiesCV.indexPathsForSelectedItems!

    }
}
}

Delete Button

@IBAction func deleteButtonPressed(_ sender: UIButton) {
    for item in didSelectIPArray {
        self.allHobbiesCV.deselectItem(at: item, animated: false) // Try deselecting the deleted items in allHobbiesCV... This is crashing.
    }
}

The issue is how I am trying to toggle the allHobbiesCV cells in didDeselect. Was my approach correct in saving the selected cells in didSelectIPArray?

I'll be happy to provide further insight if needed. Thank you much in advance friends!

There is no need for all of that map and flatmap stuff. You can simply remove the object from the selected hobbies array:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    if collectionView == allHobbiesCV {
        if myHobbiesArray.count <= 6
            self.allSelected = []
            myHobbiesArray.insert(allHobbiesArray[indexPath.item], at: 0)

            let allHobbiesCell = allHobbiesCV.cellForItem(at: indexPath) as! AllHobbiesCell
            allHobbiesCell.backgroundColor = UIColor.green

            myHobbiesCV.insertItems(at: [IndexPath(item: 0, section: 0)])
        }
    } else {
        let cell = myHobbiesCV.cellForItem(at: indexPath) as! MyHobbiesCell
        cell.backgroundColor = UIColor.red
        allSelected = self.myHobbiesCV.indexPathsForSelectedItems!
    }
}

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

    if collectionView == allHobbiesCV {

        // Yellow is the unselected cell color. So let's change it back to yellow.
        let allHobbiesCell = allHobbiesCV.cellForItem(at: indexPath) as! AllHobbiesCell
        allHobbiesCell.backgroundColor = UIColor.yellow

        let hobby = allHobbiesArray[indexPath.item]

        if let index = myHobbiesArray.index(of:hobby) {
             myHobbiesArray.remove(at: index)
             myHobbiesCV.deleteItems(at: [IndexPath(item: index, section:0)])
        }
    } else { // MyHobbies CV
        let cell = myHobbiesCV.cellForItem(at: indexPath) as! MyHobbiesCell
        cell.backgroundColor = UIColor.yellow

        // Store the selected cells to be manually deleted.
        allSelected = self.myHobbiesCV.indexPathsForSelectedItems!

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM