Search code examples
swiftuiimageviewuicollectionviewuiimagedata-transfer

How to select multiple images from UICollectionView and transfer them to another View Controller?


The code is written in Swift. I'm building a social app where the user can make posts. I'm using Firebase as a backend (database, storage). So, I have a UICollectionView that gets all the photos from the photo library of the device and populate the collection view using a custom cell. In the same View controller, I have another custom cell that the user can use to take a photo and use it to make a post. To make it clearer:

  • If the user decides to take a photo, when they click on "Use photo" they need to be presented to a new view controller that should display the photo they just took along with other options (such as title, description and tags using UITextFields & UITextView).

  • If the user decides to select multiple photos from their own library, I have to somehow mark those photos/cells (i.e. using a button for a checkmark), add the selected photos to an array (with some limit, maybe 10 photos top). When they click "Next" button, the array needs to be sent to the New Post View Controller where all the images should be dynamically displayed maybe using a horizontal UICollectionView (?!) (with an option to remove an image if it was selected by accident) and again, as above, have the opportunity to add title, description, etc. Now, I cannot figure out how to do any of that.

I looked for a solution, but I'm kind of stuck on this for couple days now, so help is very much welcome!

Here's what I have in the Collection View controller (PS: I didn't include the part with the function that gets the images from the Photos)

import UIKit
import Photos

class PrePhotoPostVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UICollectionViewDelegateFlowLayout {

@IBOutlet weak var nextButton: UIBarButtonItem!
var photosLibraryArray = [UIImage]()

@IBOutlet weak var collectionView: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()

    checkPhotoLibraryPermission()
    setupCollectionViewDelegates()
}

@IBAction func cancelButtonPressed (_ sender: UIBarButtonItem) {
    dismiss(animated: true, completion: nil)
}

@IBAction func nextButtonPressed (_ sender: UIBarButtonItem) {
    nextButton.isEnabled = false
}

@IBAction func takeAphotoButtonPressed (_ sender: UIButton) {

    // Camera Autorization
    AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { response in
        if response {
            if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) {
                let imagePicker = UIImagePickerController()
                imagePicker.delegate = self
                imagePicker.sourceType = UIImagePickerControllerSourceType.camera;
                imagePicker.allowsEditing = false
                self.present(imagePicker, animated: true, completion: nil)
            }
            else {
                print("Camera isn't available in similator")
            }
        }
        else {
            print("unautorized")
        }
    }
}

 func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 2
}
 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if section == 0 {
        return 1
    } else {
        return photosLibraryArray.count
    }
 }

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

    if indexPath.section == 0 {
        let cellCamera = collectionView.dequeueReusableCell(withReuseIdentifier: cellPrePostCameraCell, for: indexPath) 
        return cellCamera
    }
    else {
        let cellPhotoLibrary = collectionView.dequeueReusableCell(withReuseIdentifier: cellPrePostPhotoLibrary, for: indexPath) as! PrePhotoPostPhotoLIbraryCell
        cellPhotoLibrary.awakeFromNib()
        cellPhotoLibrary.photoLibraryImage.image = photosLibraryArray[indexPath.row]
        return cellPhotoLibrary
    }
}
}

A screenshot of what this UICollectionView looks like:

enter image description here

Here's my code from the Photo Library Cell:

import UIKit

class PrePhotoPostPhotoLIbraryCell: UICollectionViewCell {

    // MARK: Outlets
    @IBOutlet weak var photoLibraryImage: UIImageView!

    // var selectedPhotos = [UIImageView]()

    @IBAction func selectedButtonPressed(_ sender: UIButton) {
        self.layer.borderWidth = 3.0
        self.layer.borderColor = isSelected ? UIColor.blue.cgColor : UIColor.clear.cgColor
    }
    override func awakeFromNib() {

        photoLibraryImage.clipsToBounds = true
        photoLibraryImage.contentMode = .scaleAspectFill
        photoLibraryImage.layer.borderColor = UIColor.clear.cgColor
        photoLibraryImage.layer.borderWidth = 1
        photoLibraryImage.layer.cornerRadius = 5

    }
}

Solution

  • First of all declare an array of mutable type that will store the selected cells item in it.

    var _selectedCells : NSMutableArray = []
    

    then in your viewDidLoad function add below code.

     override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            //this will allow multiple selection on uicollectionviewcell
            CollectionView.allowsMultipleSelection=true //CollectionView is your CollectionView outlet
    }
    

    Then, Implement delegate functions of collectionview for selecting and deselecting cells

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
    
            //add the selected cell contents to _selectedCells arr when cell is selected
            _selectedCells.add(indexPath)
            collectionView.reloadItems(at: [indexPath])
        }
    
        func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    
            //remove the selected cell contents from _selectedCells arr when cell is De-Selected
    
            _selectedCells.remove(indexPath)
            collectionView.reloadItems(at: [indexPath])
        }
    

    I'd suggest saving the NSIndexPath of the selected item in an array, and then using that for the basis of comparison in the delegate function cellForItemAt indexPath.

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YOUR_CELL_Identifier", for: indexPath as IndexPath)
    
    //add your tick mark image to the cell in your storyboard or xib file.
    let tickImage = cell.viewWithTag(YOUR_IMAGE_TAG_HERE) as? UIImageView
    
    //Show tickImage if the cell is selected and hide tickImage if cell is NotSelected/deSelected.or whatever action you want to perform in case of selection and deselection of cell.
    
    if _selectedCells.contains(indexPath) {
    cell.isSelected=true
    collectionView.selectItem(at: indexPath, animated: true, scrollPosition: UICollectionViewScrollPosition.top)
    tickImage?.isHidden=false
    }
    else{
    cell.isSelected=false
    tickImage?.isHidden=true
    }
    return cell
    }
    

    In Order to send items to next controller, get all the items from selected indexpaths.