Search code examples
swiftuikitviewcontrollercollectionview

How do I create multiple collection views with the same behavior in one view controller?


I'm making an app where users can upload photos of different types of clothing items and have it be displayed in a collection view. Users can click on a button from a drop-down menu to tell the program what section the image should be put under. I originally only needed one collection view. But now I decided to expand to multiple, but I only have the code for one. Is there a good way to adapt my already written code so that the other collection views will have the same behavior as the first?

//
//  ViewController.swift
//  ugh as if
//
//

import UIKit
import PhotosUI
import Photos
import CoreData
import Foundation
import SwiftUI

// user images below
var imageIDs = [String]()
var countImage = Int()
var currentImageType = String()

class ViewController: UIViewController, PHPickerViewControllerDelegate {

    // when trash is pressed in ClsoetDetailViewController, return to ViewController
    @IBAction func unwindToCloset(segue: UIStoryboardSegue) {
        self.collectionView.reloadData()
    }
    
    
    @IBOutlet var collectionView: UICollectionView!
    @IBOutlet var outerwearCollectionView: UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        
        // popup menu items
        let tops = UIAction(title: "tops") { (action) in
              print("tops!")
            self.addPhotos(categoryType: "tops")
         }
         let outerwear = UIAction(title: "outerwear") { (action) in
             print("outerwear!")
             self.addPhotos(categoryType: "outerwear")
         }
         let bottoms = UIAction(title: "bottoms") { (action) in
              print("bottoms!")
             self.addPhotos(categoryType: "bottoms")
         }
        let singles = UIAction(title: "one pieces") { (action) in
             print("singles!")
            self.addPhotos(categoryType: "singles")
        }
        let accessories = UIAction(title: "accessories") { (action) in
             print("accessories!")
            self.addPhotos(categoryType: "accessories")
        }
        let shoes = UIAction(title: "shoes") { (action) in
             print("shoes!")
            self.addPhotos(categoryType: "shoes")
        }
        let menu = UIMenu(title: "my closet", options: .displayInline,
                           children: [tops , outerwear , bottoms, singles, shoes, accessories])
        
        // set up collection in closet
        // navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPhotos))
        navigationItem.rightBarButtonItems = [UIBarButtonItem(systemItem: .add, menu: menu)]
        
        collectionView.register(ClosetCollectionViewCell.nib(), forCellWithReuseIdentifier: "ClosetCollectionViewCell")
        
        // layout.minimumInteritemSpacing = 0
        
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: 75, height: 100)
        layout.minimumLineSpacing = 15
        
        collectionView.collectionViewLayout = layout
        
        collectionView.delegate = self
        collectionView.dataSource = self
    
        collectionView.reloadData()
    }
    
    
    // access photo library
    @objc private func addPhotos(categoryType: String) {
        var config = PHPickerConfiguration()
        config.selectionLimit = 100
        config.filter = .images
        let vc = PHPickerViewController(configuration: config)
        vc.delegate = self
        present(vc, animated: true)
        currentImageType = categoryType
    }
    
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true, completion: nil)
        let group = DispatchGroup()
        results.forEach { result in
            group.enter()
            result.itemProvider.loadObject(ofClass: UIImage.self) { reading, error in
                defer {
                    group.leave()
                }
                guard let image = reading as? UIImage, error == nil else {
                    return
                }
                
                countImage += 1
                print(countImage)
                
                imageIDs.append(String(countImage))
                print(imageIDs)
                
                LocalFileManager.instance.saveImage(image: image, imageName: String(countImage), folderName: currentImageType)
            }
        }
        group.notify(queue: .main) {
            self.collectionView.reloadData()
        }
    }

}

extension ViewController: UICollectionViewDelegate {
func collectionView(\_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
print("you tapped me!")
// set closetImageName in ClosetDetailViewController
detailImageName = imageIDs\[indexPath.row\]

        print(imageIDs[indexPath.row])
        performSegue(withIdentifier: "closetDetail", sender: nil)
    }

}

extension ViewController: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // how many cells are shown? based on number of items the user uploaded
        return countImage
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // return cell for given item
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ClosetCollectionViewCell", for: indexPath) as! ClosetCollectionViewCell
        // show every cell in image array
        cell.imageView.image = LocalFileManager.instance.getImage(imageName: imageIDs[indexPath.row], folderName: "tops")
        return cell
    }

}

extension ViewController: UICollectionViewDelegateFlowLayout {
// margin of padding between cells
func collectionView(\_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -\> CGSize {
return CGSize(width: 75, height: 100)
}

}

Below is what the collection views look like, but with what I have now, all the photos only get sent to the "tops" collection

collection views preview!

I'm still pretty new to Xcode and have only been working with it for a few months, so any help is appreciated!

I tried duplicating and editing the functions in the extensions (found below picker) so that I would be able to send the different photos to different locations, but it kept throwing errors at me.

extension ViewController: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // how many cells are shown? based on number of items the user uploaded
        return countImage
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // return cell for given item
        if collectionView == self.collectionView {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ClosetCollectionViewCell", for: indexPath) as! ClosetCollectionViewCell
            // show every cell in image array
            cell.imageView.image = LocalFileManager.instance.getImage(imageName: imageIDs[indexPath.row], folderName: "tops")
            return cell
        }
        else {
            let cell = outerwearCollectionView.dequeueReusableCell(withReuseIdentifier: "ClosetCollectionViewCell", for: indexPath) as! ClosetCollectionViewCell
            cell.imageView.image = LocalFileManager.instance.getImage(imageName: imageIDs[indexPath.row], folderName: "outerwear")
            return cell
        }
    }
}

Solution

  • You current collection view is only using 1 section. Instead of adding multiple collection views you can make use of multiple sections in the collection view you have. You just need to organize your data source from being an array to an array of arrays.

    You'll need to adjust the details but let's say your data becomes:

    var images = [[UIImage]]() // Or an array of array of image ids
    

    Then your collection view methods become something like this:

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return images.count
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return images[section].count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = ... // however you get a cell
        cell.imageView.image = images[indexPath.section][indexPath.item]
    
        return cell
    }
    

    Carry on that general pattern for the other methods.

    You can setup the collection view with section headers if desired to show a category title or something.

    This is all much simpler than adding multiple collection views if the functionality is basically the same for all.