UICollectionView didSelectItemAt

Hi, I've been trying to set the "photoImageView.image" to the image "cell.bgImage" within the collectionView indexPath based on the selection. With a tap on the collectionView's image cells, I want the photoImageView.image to change according to the tapped image cell. I have tried many ways but couldn't get success. I believe the most feasible way is implementing this in "collectionView didSelectItemAt". Any help please ?

Thank you.


import Foundation

import UIKit

import Photos



class PhotoViewController: UIViewController {


@IBOutlet weak var photoImageView: UIImageView!

@IBOutlet var photoView: UIView!

@IBOutlet weak var menuStackView: UIStackView!


var imagePicker = UIImagePickerController()

var effects = Effects()


override func viewDidLoad() {

super.viewDidLoad()

imagePicker.delegate = self

assignTap()

view.addSubview(collectionView)

collectionView.backgroundColor = .white

collectionView.topAnchor.constraint(equalTo: photoImageView.bottomAnchor, constant: 110).isActive = true

collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true

collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 10).isActive = true

collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -103).isActive = true

collectionView.clipsToBounds = true

collectionView.dataSource = self

collectionView.delegate = self

collectionView.isHidden = false

}


@IBAction func previewTapped(_ sender: UIButton) {

sender.pulsate()

if collectionView.isHidden == true {

collectionView.isHidden = false

} else {

collectionView.isHidden = true

}

}


@IBAction func effectsTapped(_ sender: UIButton) {

sender.flash()

}


@IBAction func exportTapped(_ sender: UIButton) {

sender.shake()

}


func assignTap() {

let tap = UITapGestureRecognizer(target: self, action: #selector(touchTapped(_:)))

photoView.addGestureRecognizer(tap)

}


@objc func touchTapped(_ sender: UITapGestureRecognizer) {

if collectionView.isTracking == false {

if menuStackView.isHidden == false {

collectionView.isHidden = true

menuStackView.isHidden = true

} else {

menuStackView.isHidden = false

collectionView.isHidden = false

}

}

}


func chooseImage(_ sender: Any) {

if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {

imagePicker.sourceType = .photoLibrary

imagePicker.allowsEditing = true

self.present(imagePicker,animated: true, completion: nil)

}

}


func accessVideo(_ sender: Any) {

if UIImagePickerController.isSourceTypeAvailable(.camera) {

imagePicker.sourceType = .camera

self.present(imagePicker, animated: true, completion: nil)

} else {

print("Camera not available")

}

}


public func chooseLastImage(_ sender: Any) {

print("photoImageView", photoImageView!)

print("photoImageView.bounds", photoImageView.bounds)

let targetSize: CGSize? = CGSize(width: photoImageView.bounds.width, height: photoImageView.bounds.height)

let options = PHImageRequestOptions()

options.isNetworkAccessAllowed = true

options.deliveryMode = .highQualityFormat

let imgManager = PHImageManager.default()

let fetchOptions = PHFetchOptions()

fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]

let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)

print("targetSize for request", targetSize!)

imgManager.requestImage(for: fetchResult.lastObject!, targetSize: targetSize!, contentMode: .default, options: options) { (result, info) in

guard let result = result else { return }

DispatchQueue.main.async(execute: {

self.photoImageView.image = result

self.collectionView.reloadData()

})

}

}


fileprivate let collectionView: UICollectionView = {

let layout = UICollectionViewFlowLayout()

layout.scrollDirection = .horizontal

let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)

cv.translatesAutoresizingMaskIntoConstraints = false

cv.register(CustomCell.self, forCellWithReuseIdentifier: "PreviewCell")

return cv

}()


}


extension PhotoViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

return CGSize(width: collectionView.frame.width/5, height: collectionView.frame.width/4)

}


func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

return 11

}


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

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PreviewCell", for: indexPath) as! CustomCell

cell.bgImage = self.photoImageView.image

if indexPath.row == 0 {

cell.labelText = "A"

}

if indexPath.row == 1 {

cell.labelText = "B"

guard cell.bgImage != nil else {

return cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectChrome", filterEffectValue: nil, filterEffectValueName: nil))

}

if indexPath.row == 2 {

cell.labelText = "C"

guard cell.bgImage != nil else {

return cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectInstant", filterEffectValue: nil, filterEffectValueName: nil))

}

if indexPath.row == 3 {

cell.labelText = "D"

guard cell.bgImage != nil else {

return cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectNoir", filterEffectValue: nil, filterEffectValueName: kCIInputImageKey))

}

if indexPath.row == 4 {

cell.labelText = "E"

guard cell.bgImage != nil else {

return cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectMono", filterEffectValue: nil, filterEffectValueName: kCIInputImageKey))

}

if indexPath.row == 5 {

cell.labelText = "F"

guard cell.bgImage != nil else {

return cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIExposureAdjust", filterEffectValue: 1, filterEffectValueName: kCIInputEVKey))

}

if indexPath.row == 6 {

cell.labelText = "G"

guard cell.bgImage != nil else {

return cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CISRGBToneCurveToLinear", filterEffectValue: nil, filterEffectValueName: nil))

}

if indexPath.row == 7 {

cell.labelText = "H"

guard cell.bgImage != nil else { return cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectTransfer", filterEffectValue: nil, filterEffectValueName: nil))

}

if indexPath.row == 8 {

cell.labelText = "I"

guard cell.bgImage != nil else { return

cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIColorPosterize", filterEffectValue: nil, filterEffectValueName: nil))

}

if indexPath.row == 9 {

cell.labelText = "J"

guard cell.bgImage != nil else { return

cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectProcess", filterEffectValue: nil, filterEffectValueName: nil))

}

if indexPath.row == 10 {

cell.labelText = "K"

guard cell.bgImage != nil else { return

cell

}

cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIVignetteEffect", filterEffectValue: nil, filterEffectValueName: nil))

}

return cell

}


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {

return UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 5)

}


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {

return 1

}


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

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PreviewCell", for: indexPath) as! CustomCell

// cell.bgImage = self.photoImageView.image

let imgArr = [cell.bgImage]

photoImageView.image = imgArr[indexPath.row]

photoImageView.reloadInputViews()

}

}


class CustomCell: UICollectionViewCell {


fileprivate let bg: UIImageView = {

let iv = UIImageView()

iv.translatesAutoresizingMaskIntoConstraints = false

iv.contentMode = .scaleAspectFit

iv.clipsToBounds = true

return iv

}()


var bgImage: UIImage? {

get {

bg.image

}

set {

bg.image = newValue

}

}


fileprivate let label: UILabel = {

let il = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))

il.translatesAutoresizingMaskIntoConstraints = false

il.contentMode = .scaleAspectFit

il.clipsToBounds = true

return il

}()


var labelText: String? { // ####

get {

label.text

}

set {

label.text = newValue

}

}


override init(frame: CGRect) {

super.init(frame: frame)

contentView.addSubview(bg)

contentView.addSubview(label)

bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true

bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true

bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true

bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

}


required init?(coder: NSCoder) {

fatalError("init(coder:) has not been implemented")

}

}


extension PhotoViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {


func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {

photoImageView.image = image

}

dismiss(animated: true, completion: nil)

}


func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {

dismiss(animated: true, completion: nil)

}


}

You should format the code with <>, that makes it much more easy to track.


Yes, you're right, that should be done in didSelectItemAt.


You wrote: couldn't get success.

- What did you expect ?

- what do you get ?


Just to make sure:

- all images in cells are the same (line 142: cell.bgImage = self.photoImageView.image)

- with a different effect.


What do you want ?

- get the same image effect in self.photoImageView.image ?

- but then, the image line 142 will be modified and effects will be applied multiple times.


It is not a good way tou use cells as a storage.

You always need to return to the dataSource.

So here, if you want to display the image with the effect,

- You need to keep somewhere the raw image (before effect) ; load it in chooseLastImage and chooseImage

- So, you should write a func to apply an effect to an image, depending on a parameter

- and use this func in cellAtRow as well as in didSelectItemAt, to apply the effect on the raw image, depending on selected cell


import Foundation
import UIKit
import Photos

class PhotoViewController: UIViewController {

  @IBOutlet weak var photoImageView: UIImageView!
  @IBOutlet var photoView: UIView!
  @IBOutlet weak var menuStackView: UIStackView!

  var imagePicker = UIImagePickerController()
  var effects = Effects()

  override func viewDidLoad() {
    super.viewDidLoad()
  
    imagePicker.delegate = self
    assignTap()
  
    view.addSubview(collectionView)
    collectionView.backgroundColor = .white
    collectionView.topAnchor.constraint(equalTo: photoImageView.bottomAnchor, constant: 110).isActive = true
    collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
    collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 10).isActive = true
    collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -103).isActive = true
  
    collectionView.clipsToBounds = true
  
    collectionView.dataSource = self
    collectionView.delegate = self
    collectionView.isHidden = false
  }

  @IBAction func previewTapped(_ sender: UIButton) {
    sender.pulsate()
    if collectionView.isHidden == true {
      collectionView.isHidden = false
    } else {
      collectionView.isHidden = true
    }
  }

  @IBAction func effectsTapped(_ sender: UIButton) {
    sender.flash()
  }

  @IBAction func exportTapped(_ sender: UIButton) {
    sender.shake()
  }

  func assignTap() {
    let tap = UITapGestureRecognizer(target: self, action: #selector(touchTapped(_:)))
    photoView.addGestureRecognizer(tap)
  
  }

  @objc func touchTapped(_ sender: UITapGestureRecognizer) {
    if collectionView.isTracking == false {
      if menuStackView.isHidden == false {
        collectionView.isHidden = true
        menuStackView.isHidden = true
      } else {
        menuStackView.isHidden = false
        collectionView.isHidden = false
      }
    }
  }

  func chooseImage(_ sender: Any) {
  
    if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
      imagePicker.sourceType = .photoLibrary
      imagePicker.allowsEditing = true
      self.present(imagePicker,animated: true, completion: nil)
    }
  }

  func accessVideo(_ sender: Any) {
  
    if UIImagePickerController.isSourceTypeAvailable(.camera) {
      imagePicker.sourceType = .camera
      self.present(imagePicker, animated: true, completion: nil)
    } else {
      print("Camera not available")
    }
  }

  public func chooseLastImage(_ sender: Any) {
  
    print("photoImageView", photoImageView!)
    print("photoImageView.bounds", photoImageView.bounds)
  
    let targetSize: CGSize? = CGSize(width: photoImageView.bounds.width, height: photoImageView.bounds.height)
    let options = PHImageRequestOptions()
    options.isNetworkAccessAllowed = true
    options.deliveryMode = .highQualityFormat
  
    let imgManager = PHImageManager.default()
    let fetchOptions = PHFetchOptions()
    fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
  
    let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
  
    print("targetSize for request", targetSize!)
    imgManager.requestImage(for: fetchResult.lastObject!, targetSize: targetSize!, contentMode: .default, options: options) { (result, info) in
    
      guard let result = result else { return }
    
      DispatchQueue.main.async(execute: {
        self.photoImageView.image = result
        self.collectionView.reloadData()
      })
    }
  }

  fileprivate let collectionView: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
    cv.translatesAutoresizingMaskIntoConstraints = false
    cv.register(CustomCell.self, forCellWithReuseIdentifier: "PreviewCell")
  
    return cv
  }()

}

extension PhotoViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: collectionView.frame.width/5, height: collectionView.frame.width/4)
  }

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

  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PreviewCell", for: indexPath) as! CustomCell
  
    cell.bgImage = self.photoImageView.image
  
    if indexPath.row == 0 {
      cell.labelText = "A"
    }
    if indexPath.row == 1 {
      cell.labelText = "B"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectChrome", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 2 {
      cell.labelText = "C"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectInstant", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 3 {
      cell.labelText = "D"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectNoir", filterEffectValue: nil, filterEffectValueName: kCIInputImageKey))
    }
    if indexPath.row == 4 {
      cell.labelText = "E"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectMono", filterEffectValue: nil, filterEffectValueName: kCIInputImageKey))
    }
    if indexPath.row == 5 {
      cell.labelText = "F"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIExposureAdjust", filterEffectValue: 1, filterEffectValueName: kCIInputEVKey))
    }
    if indexPath.row == 6 {
      cell.labelText = "G"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CISRGBToneCurveToLinear", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 7 {
      cell.labelText = "H"
      guard cell.bgImage != nil else { return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectTransfer", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 8 {
      cell.labelText = "I"
      guard cell.bgImage != nil else { return
        cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIColorPosterize", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 9 {
      cell.labelText = "J"
      guard cell.bgImage != nil else { return
        cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectProcess", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 10 {
      cell.labelText = "K"
      guard cell.bgImage != nil else { return
        cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIVignetteEffect", filterEffectValue: nil, filterEffectValueName: nil))
    }
  
    return cell
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 5)
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 1
  }

  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PreviewCell", for: indexPath) as! CustomCell
    //    cell.bgImage = self.photoImageView.image
    let imgArr = [cell.bgImage]
    photoImageView.image = imgArr[indexPath.row]
  
    photoImageView.reloadInputViews()
  
  }
}

class CustomCell: UICollectionViewCell {

  fileprivate let bg: UIImageView = {
    let iv = UIImageView()
    iv.translatesAutoresizingMaskIntoConstraints = false
    iv.contentMode = .scaleAspectFit
    iv.clipsToBounds = true
    return iv
  }()

  var bgImage: UIImage? {
    get {
      bg.image
    }
    set {
      bg.image = newValue
    }
  }

  fileprivate let label: UILabel = {
    let il = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
    il.translatesAutoresizingMaskIntoConstraints = false
    il.contentMode = .scaleAspectFit
    il.clipsToBounds = true
    return il
  }()

  var labelText: String?  { // ####
    get {
      label.text
    }
    set {
      label.text = newValue
    }
  }

  override init(frame: CGRect) {
    super.init(frame: frame)
  
    contentView.addSubview(bg)
    contentView.addSubview(label)
  
    bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
    bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
    bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
  
    label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
  
  }

  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

extension PhotoViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
  
    if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
    
      photoImageView.image = image
    }
    dismiss(animated: true, completion: nil)
  
  }

  func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    dismiss(animated: true, completion: nil)
  }

}

How many times have you posted questions or replies in this site? It seems to be a good time for you to use code insertion feature (the icon `< >`) properly.


`dequeueReusableCell(withReuseIdentifier:for:)` is a method only for use `collectionView(_:cellForItemAt:)`,

Use `cellForItem(at:)` in all other places.

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard let cell = collectionView.cellForItem(at: indexPath) as? CustomCell else {
            return
        }
        photoImageView.image = cell.bgImage
        
        photoImageView.reloadInputViews()
    }

I feel like I'm getting close. Any feedback please ?


import Foundation
import UIKit
import Photos

class PhotoViewController: UIViewController {
 
  @IBOutlet weak var photoImageView: UIImageView!
  @IBOutlet var photoView: UIView!
  @IBOutlet weak var menuStackView: UIStackView!
 
  var imagePicker = UIImagePickerController()
  var effects = Effects()
  var rawImage: UIImage? {
    get {
      photoImageView.image
    }
    set {
      photoImageView.image = newValue
    }
  }
 
  override func viewDidLoad() {
    super.viewDidLoad()
   
    imagePicker.delegate = self
    assignTap()
   
    view.addSubview(collectionView)
    collectionView.backgroundColor = .white
    collectionView.topAnchor.constraint(equalTo: photoImageView.bottomAnchor, constant: 110).isActive = true
    collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
    collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 10).isActive = true
    collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -103).isActive = true
   
    collectionView.clipsToBounds = true
   
    collectionView.dataSource = self
    collectionView.delegate = self
    collectionView.isHidden = false
   
  }
 
  @IBAction func previewTapped(_ sender: UIButton) {
    sender.pulsate()
    if collectionView.isHidden == true {
      collectionView.isHidden = false
    } else {
      collectionView.isHidden = true
    }
  }
 
  @IBAction func effectsTapped(_ sender: UIButton) {
    sender.flash()
  }
 
  @IBAction func exportTapped(_ sender: UIButton) {
    sender.shake()
  }
 
 
 
  func assignTap() {
    let tap = UITapGestureRecognizer(target: self, action: #selector(touchTapped(_:)))
    photoView.addGestureRecognizer(tap)
   
  }
 
  @objc func touchTapped(_ sender: UITapGestureRecognizer) {
    if collectionView.isTracking == false {
      if menuStackView.isHidden == false {
        collectionView.isHidden = true
        menuStackView.isHidden = true
      } else {
        menuStackView.isHidden = false
        collectionView.isHidden = false
      }
    }
  }
 
  func chooseImage(_ sender: Any) {
   
    if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
      imagePicker.sourceType = .photoLibrary
      imagePicker.allowsEditing = true
      self.present(imagePicker,animated: true, completion: nil)
    }
  }
 
  func accessVideo(_ sender: Any) {
   
    if UIImagePickerController.isSourceTypeAvailable(.camera) {
      imagePicker.sourceType = .camera
      self.present(imagePicker, animated: true, completion: nil)
    } else {
      print("Camera not available")
    }
  }
 
  public func chooseLastImage(_ sender: Any) {
   
    print("photoImageView", photoImageView!)
    print("photoImageView.bounds", photoImageView.bounds)
   
    let targetSize: CGSize? = CGSize(width: photoImageView.bounds.width, height: photoImageView.bounds.height)
    let options = PHImageRequestOptions()
    options.isNetworkAccessAllowed = true
    options.deliveryMode = .highQualityFormat
   
    let imgManager = PHImageManager.default()
    let fetchOptions = PHFetchOptions()
    fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
   
    let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
   
    print("targetSize for request", targetSize!)
    imgManager.requestImage(for: fetchResult.lastObject!, targetSize: targetSize!, contentMode: .default, options: options) { (result, info) in
     
      guard let result = result else { return }
     
      DispatchQueue.main.async(execute: {
        self.rawImage = result
        self.collectionView.reloadData()
       
      })
    }
  }
 
  fileprivate let collectionView: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
    cv.translatesAutoresizingMaskIntoConstraints = false
    cv.register(CustomCell.self, forCellWithReuseIdentifier: "PreviewCell")
   
    return cv
  }()
 
}

extension PhotoViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
 
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: collectionView.frame.width/5, height: collectionView.frame.width/4)
  }
 
  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 11
  }
 
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
   
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PreviewCell", for: indexPath) as! CustomCell
   
   
    cell.bgImage = photoImageView.image
    if indexPath.row == 0 {
      cell.labelText = "A"
     
     
    }
    if indexPath.row == 1 {
      cell.labelText = "B"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectChrome", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 2 {
      cell.labelText = "C"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectInstant", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 3 {
      cell.labelText = "D"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectNoir", filterEffectValue: nil, filterEffectValueName: kCIInputImageKey))
    }
    if indexPath.row == 4 {
      cell.labelText = "E"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectMono", filterEffectValue: nil, filterEffectValueName: kCIInputImageKey))
    }
    if indexPath.row == 5 {
      cell.labelText = "F"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIExposureAdjust", filterEffectValue: 1, filterEffectValueName: kCIInputEVKey))
    }
    if indexPath.row == 6 {
      cell.labelText = "G"
      guard cell.bgImage != nil else {
        return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CISRGBToneCurveToLinear", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 7 {
      cell.labelText = "H"
      guard cell.bgImage != nil else { return cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectTransfer", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 8 {
      cell.labelText = "I"
      guard cell.bgImage != nil else { return
        cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIColorPosterize", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 9 {
      cell.labelText = "J"
      guard cell.bgImage != nil else { return
        cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectProcess", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 10 {
      cell.labelText = "K"
      guard cell.bgImage != nil else { return
        cell
      }
      cell.bgImage = effects.applyFilterTo(image: photoImageView.image!, filterEffect: Effects.Filter(filterName: "CIVignetteEffect", filterEffectValue: nil, filterEffectValueName: nil))
    }
   
    return cell
  }
 
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 5)
  }
 
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 1
  }
 
  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
   
    if indexPath.row == 0 {
     
      rawImage = photoImageView.image
    }
    if indexPath.row == 1 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectChrome", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 2 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectInstant", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 3 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectNoir", filterEffectValue: nil, filterEffectValueName: kCIInputImageKey))
    }
    if indexPath.row == 4 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectMono", filterEffectValue: nil, filterEffectValueName: kCIInputImageKey))
    }
    if indexPath.row == 5 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIExposureAdjust", filterEffectValue: 1, filterEffectValueName: kCIInputEVKey))
    }
    if indexPath.row == 6 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CISRGBToneCurveToLinear", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 7 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectTransfer", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 8 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIColorPosterize", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 9 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectProcess", filterEffectValue: nil, filterEffectValueName: nil))
    }
    if indexPath.row == 10 {
     
      rawImage = effects.applyFilterTo(image: rawImage!, filterEffect: Effects.Filter(filterName: "CIVignetteEffect", filterEffectValue: nil, filterEffectValueName: nil))
    }
   
  }
}

class CustomCell: UICollectionViewCell {
 
  fileprivate let bg: UIImageView = {
    let iv = UIImageView()
    iv.translatesAutoresizingMaskIntoConstraints = false
    iv.contentMode = .scaleAspectFit
    iv.clipsToBounds = true
    return iv
  }()
 
  var bgImage: UIImage? {
    get {
      bg.image
    }
    set {
      bg.image = newValue
    }
  }
 
  fileprivate let label: UILabel = {
    let il = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
    il.translatesAutoresizingMaskIntoConstraints = false
    il.contentMode = .scaleAspectFit
    il.clipsToBounds = true
    return il
  }()
 
  var labelText: String?  {
    get {
      label.text
    }
    set {
      label.text = newValue
    }
  }
 
  override init(frame: CGRect) {
    super.init(frame: frame)
   
    contentView.addSubview(bg)
    contentView.addSubview(label)
   
    bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
    bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
    bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
   
    label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
   
  }
 
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

extension PhotoViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
 
  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
   
    if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
     
      photoImageView.image = image
    }
    dismiss(animated: true, completion: nil)
   
  }
 
  func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    dismiss(animated: true, completion: nil)
  }
 
}

What happens with your new code?

When I cancel the "touchTapped" and "assignTap" methods, the "photoImageView.image", "rawImage" change according to the collectionView cell image but the problem is the collectionView images start changing too. If I keep the methods and add "collectionView.reloadData()" after the 76th line, the "photoImageView.image", "rawImage" don't change.


I think I need to implement a different logic for;


var rawImage: UIImage? {

get {

photoImageView.image

}

set {

photoImageView.image = newValue

}

}

I think I'm going though a retain cycle on; (but "weak" or "unowned" references don't solve the situation.)


var rawImage: UIImage? {

get {

photoImageView.image

}

set {

photoImageView.image = newValue

}

}

I have not checked or tested your code yet, but I think the most important thing in your code is that you have only one place for images, the picked image (original image) and the filtered image.


You may need to distinguish the two. Remove computed property `rawImage` and put two properties `pickedImage` and `filteredImage`:

class PhotoViewController: UIViewController {

    //...    
    
    //A place to keep the originally picked image
    var pickedImage: UIImage?
    
    //A place to keey the filtered image
    var filteredImage: UIImage?
    
    //...
}


The property `bgImage` in each cell should be made from `pickedImage`:

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PreviewCell", for: indexPath) as! CustomCell
        
        //Set bgImage of each cell made from `pickedImage`
        cell.bgImage = pickedImage
        if indexPath.row == 0 {
            cell.labelText = "A"
        }
        if indexPath.row == 1 {
            cell.labelText = "B"
            guard cell.bgImage != nil else {
                return cell
            }
            cell.bgImage = effects.applyFilterTo(image: pickedImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectChrome", filterEffectValue: nil, filterEffectValueName: nil))
        }
        //...
        if indexPath.row == 9 {
            cell.labelText = "J"
            guard cell.bgImage != nil else { return
                cell
            }
            cell.bgImage = effects.applyFilterTo(image: pickedImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectProcess", filterEffectValue: nil, filterEffectValueName: nil))
        }
        if indexPath.row == 10 {
            cell.labelText = "K"
            guard cell.bgImage != nil else { return
                cell
            }
            cell.bgImage = effects.applyFilterTo(image: pickedImage!, filterEffect: Effects.Filter(filterName: "CIVignetteEffect", filterEffectValue: nil, filterEffectValueName: nil))
        }
        
        return cell
    }

And when user have chosen a cell, set the filtered image to `filteredImage`:

    //Update filtered image when user selected a cell
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
        if indexPath.row == 0 {
            
            filteredImage = pickedImage
        }
        if indexPath.row == 1 {
            
            filteredImage = effects.applyFilterTo(image: pickedImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectChrome", filterEffectValue: nil, filterEffectValueName: nil))
        }
        //...
        if indexPath.row == 9 {
            
            filteredImage = effects.applyFilterTo(image: pickedImage!, filterEffect: Effects.Filter(filterName: "CIPhotoEffectProcess", filterEffectValue: nil, filterEffectValueName: nil))
        }
        if indexPath.row == 10 {
            
            filteredImage = effects.applyFilterTo(image: pickedImage!, filterEffect: Effects.Filter(filterName: "CIVignetteEffect", filterEffectValue: nil, filterEffectValueName: nil))
        }
        photoImageView.image = filteredImage
    }

Your image picker should update `pickedImage`:

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        
        if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            //Set picked view to `pickedImage`
            pickedImage = image
            photoImageView.image = pickedImage
        }
        dismiss(animated: true, completion: nil)
    }

Your code can be and should be refined, but that is another issue.

First make your code work as expected, and then refine it.

This is where you use tap:



  func assignTap() { 
    let tap = UITapGestureRecognizer(target: self, action: #selector(touchTapped(_:))) 
    photoView.addGestureRecognizer(tap) 
  } 
  
  @objc func touchTapped(_ sender: UITapGestureRecognizer) { 
    if collectionView.isTracking == false { 
      if menuStackView.isHidden == false { 
        collectionView.isHidden = true 
        menuStackView.isHidden = true 
      } else { 
        menuStackView.isHidden = false 
        collectionView.isHidden = false 
      } 
    } 
  }


What does it do ? What did you expect ?


It is just hiding or showing the collection, not reloading.


If I understand correctly, you should reload collection at line 15.

Now, it works with a charm. Thank you so much for your guidance. The only problem I'm having is if I keep the "assignTap()" and "touchTapped()" methods. The effect doesn't work on the "photoImageView.image". I tried this but it still doesn't work;


@objc func touchTapped(_ sender: UITapGestureRecognizer) {

if collectionView.isTracking == false {

if menuStackView.isHidden == false {

collectionView.isHidden = true

menuStackView.isHidden = true

} else {

menuStackView.isHidden = false

collectionView.isHidden = false

}

} else {

collectionView.reloadData()

}

}

When I tap outside the collectionView and the menuStackView, I want them to be hidden and then appear again. If I keep these methods, then the changes on the "photoImageView.image" don't take place with taps on the collectionView images. I tried adding "collectionView.reloadData()" at line 15 but nothing changes.

With "touchTapped()" and "assignTap()" methods being enabled, tap on the collectionView don't register as I added print("A") for indexPath.row == 0 at didSelectedItemAt.

For future reference, the solution I came up with is;


@objcfunc touchTapped(_ sender: UITapGestureRecognizer) {
    sender.cancelsTouchesInView = false

    if collectionView.isTracking == false {
      if menuStackView.isHidden == false {
        collectionView.isHidden = true
        menuStackView.isHidden = true
      } else {
        menuStackView.isHidden = false
        collectionView.isHidden = false
      }
    }
  }
UICollectionView didSelectItemAt
 
 
Q