Search code examples
swiftobjectuicollectionviewdatamodel

Update the Data Model in a CollectionView to filter the cells


I have 1 CollectionView called animalCollectionView that populates with over 100 AnimalModel objects from an AnimalData struct. Below the CollectionView, I have 4 buttons with different Animal classifications: Fish, Amphibian, Bird, Reptile. I would like these 4 buttons to toggle the appropriate data in the CollectionView. Each AnimalModel object has a property called animalSelected that should toggle accordingly.

Progress-wise, I am almost there. I am just having trouble iterating through ALL of the data to change the animalSelected property on the appropriate objects.

Here is the data:

struct AnimalModel {
    var animalName:String
    var animalClassification:Animal

    var animalSelected:Bool

    enum Animal:String {
        case bird
        case fish
        case reptile
        case amphibian
    }
}

struct AnimalData {
    static func allAnimal() -> [AnimalModel] {
        return [
            AnimalModel(animalName: "Salmon", animalClassification: .fish, animalSelected: false),
            AnimalModel(animalName: "Goldfish", animalClassification: .fish, animalSelected: false),
            AnimalModel(animalName: "Guppy", animalClassification: .fish, animalSelected: false),
            AnimalModel(animalName: "Shark", animalClassification: .fish, animalSelected: false),
            AnimalModel(animalName: "Tuna", animalClassification: .fish, animalSelected: false),
            AnimalModel(animalName: "Clownfish", animalClassification: .fish, animalSelected: false),
            AnimalModel(animalName: "Swordfish", animalClassification: .fish, animalSelected: false),
            AnimalModel(animalName: "Frog", animalClassification: .amphibian, animalSelected: false),
            AnimalModel(animalName: "Toad", animalClassification: .amphibian, animalSelected: false),
            AnimalModel(animalName: "Newt", animalClassification: .amphibian, animalSelected: false),
            AnimalModel(animalName: "Salamander", animalClassification: .amphibian, animalSelected: false),
            AnimalModel(animalName: "Ostrich", animalClassification: .bird, animalSelected: false),
            AnimalModel(animalName: "Peacock", animalClassification: .bird, animalSelected: false),
            AnimalModel(animalName: "Eagle", animalClassification: .bird, animalSelected: false),
            AnimalModel(animalName: "Duck", animalClassification: .bird, animalSelected: false),
            AnimalModel(animalName: "Chicken", animalClassification: .bird, animalSelected: false),
            AnimalModel(animalName: "Snake", animalClassification: .reptile, animalSelected: false),
            AnimalModel(animalName: "Chameleon", animalClassification: .reptile, animalSelected: false),
            AnimalModel(animalName: "Lizard", animalClassification: .reptile, animalSelected: false),
            AnimalModel(animalName: "Turtle", animalClassification: .reptile, animalSelected: false)
            // + about 80 more... 
        ]
    }
}

Here is the VC

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

    @IBOutlet weak var animalCollectionView: UICollectionView!

    @IBOutlet weak var birdOutlet: UIButton!
    @IBOutlet weak var fishOutlet: UIButton!
    @IBOutlet weak var reptileOutlet: UIButton!
    @IBOutlet weak var amphibion: UIButton!

    var all = AnimalData.allAnimal()

    override func viewDidLoad() {
        super.viewDidLoad()
        animalCollectionView.delegate = self
        animalCollectionView.dataSource = self
    }

    @IBAction func birdBtnPressed(_ sender: UIButton) {
//          for bird in all {
//              bird.animalSelected = true
//          }
        all[1].animalSelected = true
        animalCollectionView.reloadData()
    }
    @IBAction func fishBtnPressed(_ sender: UIButton) {
    }
    @IBAction func reptileBtnPressed(_ sender: UIButton) {
    }
    @IBAction func amphibionBtnPressed(_ sender: UIButton) {
    }


    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return all.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ANIMAL", for: indexPath) as! AnimalCollectionViewCell
        cell.animalNameLabel.text = all[indexPath.item].animalName
        cell.animalClassificationLabel.text = all[indexPath.item].animalClassification.rawValue

        if all[indexPath.item].animalSelected {
            cell.alpha = 1
        } else {
            cell.alpha = 0.2
        }

        return cell
    }

}

birdButtonPressed is where I was making the attempt.. I have commented out:

for bird in all {
    bird.animalSelected = true 
}

because that gives the error: Cannot assign to property: 'bird' is a 'let' constant

all[1].animalSelected = true obviously does not iterate over the birds, but I was just doing this to ensure the second cells alpha changed... which it did! (see the screenshot below)

How can I iterate over the data to change animalSelected to true on the appropriate cells?

UI


Solution

  • I would first start by pulling the Animal enum out of your AnimalModel struct for modularity. It also allows for better checking using the enum cases for equality.

    There are two things that you must do to get your value to change. Because you are using a struct, a value type instead of a reference type, you must provide a way to change its internal value. You achieve this by adding a mutating func as follows:

    struct AnimalModel {
       var animalName: String
       var animalClassification: Animal
       var animalSelected: Bool
    
       mutating func setAnimalToSelected(value: Bool) {
           animalSelected = value
       }
    }
    

    The mutating func allows you to change the value inside of a struct.

    But before doing that you must obtain the specific index of it and change that specific struct with the mutating func.

    for (index, animal) in all.enumerated() {
       if animal.animalClassification == Animal.bird {
           all[index].animalSelected = true
            //if you only have one section
           let indexPath = IndexPath(row: index, section: 0)
           self.collectionView.reloadItems(at: [indexPath])
       }
    }