Search code examples
segueuistoryboardsegueiboutletswift5xcode11

Performing Segue to a new viewcontroller - prepare for segue fails swift 5


I'm trying to perform a segue to another UIViewController.

I wish to pass data to that new controller. However, looks like the UILabels of the new UIViewController are not yet initialised, so I'm an error which states that I'm unwrapping a nil-value optional.

What am I missing?

Here's my code:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
    let cell = collectionView.cellForItem(at: indexPath)
    collectionView.deselectItem(at: indexPath, animated: true)

    performSegue(withIdentifier: "addProductSegue", sender: cell)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
    if let nextViewController = segue.destination as? AddProductsViewController
    {
        let productCell = sender as! ProductCell

        nextViewController.productName!.text = productCell.product!.Name
        nextViewController.priceForKg!.text = "\(productCell.product?.priceForKg ?? 0.0)"
        nextViewController.productImage!.image = productCell.productImageView.image
    }
}

And the new UIViewController:

import UIKit

class AddProductsViewController: UIViewController
{
    @IBOutlet weak var productName: UILabel!
    @IBOutlet weak var priceForKg: UILabel!
    @IBOutlet weak var productImage: UIImageView!

    override func viewDidLoad()
    {
        super.viewDidLoad()
    }

    @IBAction func addProduct(_ sender: UIButton)
    {
        self.navigationController?.popViewController(animated: true)
    }
}

Solution

  • you need to create separate values for each of the elements you wish to add and then use them in the second view controller

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
    {
        let cell = collectionView.cellForItem(at: indexPath)
        collectionView.deselectItem(at: indexPath, animated: true)
    
        performSegue(withIdentifier: "addProductSegue", sender: cell)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?)
    {
        if let nextViewController = segue.destination as? AddProductsViewController
        {
            let productCell = sender as! ProductCell
    
            nextViewController.productNameText = productCell.product!.Name
            nextViewController.priceForKgText = "\(productCell.product?.priceForKg ?? 0.0)"
            nextViewController.productImageImage = productCell.productImageView.image
        }
    }
    

    And in the second ViewController have:

    import UIKit
    
    class AddProductsViewController: UIViewController
    {
        @IBOutlet weak var productName: UILabel!
        @IBOutlet weak var priceForKg: UILabel!
        @IBOutlet weak var productImage: UIImageView!
    //The elements you will reference in your prepare for segue
        var productNameText = ""
        var priceForKgText = ""
        var productImageImage : UIImage?
        override func viewDidLoad()
        {
            super.viewDidLoad()
    //Call the function
            setupViews() 
        }
    
        func setupViews(){ //create a function that handles the ui update once the objects are initialized
         productName.text = productNameText
         priceForKg.text = priceForKgText
         guard let image = productImageImage else {
         return
         }
         productImage.image = image
        }
    
        @IBAction func addProduct(_ sender: UIButton)
        {
            self.navigationController?.popViewController(animated: true)
        }
    }