Accessing another viewController's image from CustomCell Class

I would like to access "photoImageView" outlet or the function "chooseLastImage" from the "CustomCell" class, "let iv = UIImageView()", in order to have the same picture on "photoImageView" ,and "let iv = UIImageView()" in the collectionView. Any advice please ?


import Foundation

import UIKit

import Photos


class PhotoViewController: UIViewController {


@IBOutlet weak var photoImageView: UIImageView!

@IBOutlet weak var peeTabBar: UITabBar!

@IBOutlet var photoView: UIView!


var imagePicker = UIImagePickerController()


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: 0).isActive = true

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

collectionView.bottomAnchor.constraint(equalTo: peeTabBar.topAnchor, constant: -20).isActive = true


collectionView.delegate = self

collectionView.dataSource = self

}


func assignTap() {

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

photoView.addGestureRecognizer(tap)

}


@objc func touchTapped(_ sender: UITapGestureRecognizer) {

peeTabBar.isHidden = !peeTabBar.isHidden

}

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!) //What is its value

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

})

}

}


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/5)

}

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.backgroundColor = .red

return cell

}

}


class CustomCell: UICollectionViewCell {


fileprivate let bg: UIImageView = {

let iv = UIImageView()


iv.translatesAutoresizingMaskIntoConstraints = false

iv.contentMode = .scaleAspectFill

iv.clipsToBounds = true

return iv

}()



override init(frame: CGRect) {

super.init(frame: frame)


contentView.addSubview(bg)

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

}


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)

}


}

Replies

A simple thing to do is to keep a reference of the PhotoViewController in the cell:


class CustomCell: UICollectionViewCell {
    var photoViewController : PhotoViewController?


You set it in the delegate func:

  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PreviewCell", for: indexPath) as! CustomCell
    cell.backgroundColor = .red
    cell.photoViewController = self
    return cell
  }
}

Then you can access to any property or func in PhotoViewController you want:


photoViewController?.someFunc() …

photoViewController?.someproperty …


You could also use delegates, but I finc this solution pretty simple.

Now, I get an empty collectionView. I guess the only way to make this work is;


class CustomCell: UICollectionViewCell {


var photoViewController: PhotoViewController?


fileprivate let bg: UIImageView = {

let iv = UIImageView()

iv.image = ?

iv.translatesAutoresizingMaskIntoConstraints = false

iv.contentMode = .scaleAspectFill

iv.clipsToBounds = true

return iv

}()


My new code;


import Foundation

import UIKit

import Photos


class PhotoViewController: UIViewController {


@IBOutlet weak var photoImageView: UIImageView!

@IBOutlet weak var peeTabBar: UITabBar!

@IBOutlet var photoView: UIView!


var imagePicker = UIImagePickerController()


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: 0).isActive = true

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

collectionView.bottomAnchor.constraint(equalTo: peeTabBar.topAnchor, constant: -20).isActive = true


collectionView.delegate = self

collectionView.dataSource = self

}


func assignTap() {

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

photoView.addGestureRecognizer(tap)

}


@objc func touchTapped(_ sender: UITapGestureRecognizer) {

peeTabBar.isHidden = !peeTabBar.isHidden

}

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!) //What is its value

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

})

}

}


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/5)

}

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.photoViewController = self

return cell

}

}


class CustomCell: UICollectionViewCell {


var photoViewController: PhotoViewController?


fileprivate let bg: UIImageView = {

let iv = UIImageView()

iv.translatesAutoresizingMaskIntoConstraints = false

iv.contentMode = .scaleAspectFill

iv.clipsToBounds = true

return iv

}()



override init(frame: CGRect) {

super.init(frame: frame)


contentView.addSubview(bg)

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

}


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)

}


}

I edited you first code to remove what is not relevant here:


You write: I get an empty collectionView

What do you mean ?

- collectionView is empty ? You should see 11 cells drawn

- or its cells are empty ? : you need to complete line 108 to set the proper image





import Foundation
import UIKit
import Photos

class PhotoViewController: UIViewController {

  @IBOutlet weak var photoImageView: UIImageView!
  @IBOutlet var photoView: UIView!
  var imagePicker = UIImagePickerController()

  override func viewDidLoad() {
    super.viewDidLoad()

    imagePicker.delegate = self

    view.addSubview(collectionView)
    collectionView.delegate = self
    collectionView.dataSource = self
  }

  func chooseImage(_ sender: Any) {

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


  public func chooseLastImage(_ sender: Any) {

      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!)          //What is its value
      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
            })

      }
    }


  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/5)
  }

  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.backgroundColor = .red
    cell.photoViewController = self // Add this
    return cell
  }
}

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)
  }

}

class CustomCell: UICollectionViewCell {

    var photoViewController : PhotoViewController?

  fileprivate let bg: UIImageView = {
    let iv = UIImageView()
     // Use photoViewController? here

    iv.translatesAutoresizingMaskIntoConstraints = false
    iv.contentMode = .scaleAspectFill
    iv.clipsToBounds = true
    return iv
  }()

  override init(frame: CGRect) {
    super.init(frame: frame)
    contentView.addSubview(bg)
  }

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

I would like to have the same picture on the photoImageView and my collectionView imageView cells. The photoImageView receives its photo from the "chooseLastImage" func. When I migrate the "chooseLastImage" func into the CustomCell, I'm having problems with its parameter(_ sender: Any). I think it's impossible to access the picture from the photoImageView outlet.

Please answer the questions, otherwise we go nowhere.


1. Previous question.

You write: I get an empty collectionView

What do you mean ?

- collectionView is empty ? You should see 11 cells drawn

- or its cells are empty ? : you need to complete line 108 to set the proper image


2. New question.

You write: I would like to have the same picture on the photoImageView and my collectionView imageView cells.

Do you mean the same image in all cells ?

That should be easy.


  fileprivate let bg: UIImageView = {
    let iv = UIImageView()     // NEED TO CREATE image with its frame
     // Use photoViewController? here
     iv.image = photoViewController?.photoImageView.image     // If I understood your code correctly

I first set "cell.backgroundColor = .red" to be able to see the cells imageView working and yes I was seeing 11 red cells drawn. I removed that line to load the same image of "photoImageView". The issue I'm having is I just can't figure out how to set it up on line 108, iv.image = ?

The simplest would probably be:




  fileprivate let bg: UIImageView = {
    let iv = UIImageView(image: photoViewController?.photoImageView.image)  

I get the following error "Instance member 'photoViewController' cannot be used on type 'CustomCell'"


class CustomCell: UICollectionViewCell {


var photoViewController: PhotoViewController?


fileprivate let bg: UIImageView = {

let iv = UIImageView(image: photoViewController?.photoImageView.image)


iv.translatesAutoresizingMaskIntoConstraints = false

iv.contentMode = .scaleAspectFill

iv.clipsToBounds = true

return iv

}()

First of all, you should not put a property with strong reference to the `PhotoViewController` in the `CustomCell`.

That makes a strong reference cycle and you would suffer with a severe memory leak.


And that is a very bad design considering the dependecy of the classes.


If you just want to set the same image kept in the property `photoImageView`, you can set it in `collectionView(_:cellForItemAt:)`.

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PreviewCell", for: indexPath) as! CustomCell
        cell.backgroundColor = .red
        cell.bgImage = photoImageView.image
        return cell

To make the above code work, you need to define a computed property `bgImage` in your `CustomCell`.

class CustomCell: UICollectionViewCell {
    
    fileprivate let bg: UIImageView = {
        let iv = UIImageView()
        
        iv.translatesAutoresizingMaskIntoConstraints = false
        iv.contentMode = .scaleAspectFill
        iv.clipsToBounds = true
        return iv
    }()
    
    var bgImage: UIImage? {
        get {
            bg.image
        }
        set {
            bg.image = newValue
        }
    }
    
    //...
}

Amazing, thank you so much. God bless!