LampPost
LampPost

Reputation: 866

What is the difference between Segue and using instantiateViewController?

What is the difference between segue and instantiateViewController?

I've been trying to figure out how to use segues to send an image from one view controller to another and 2 answers (Passing Image to another View Controller (Swift) and How do I segue an image to another ViewController and display it within an ImageView?) both say to use segues but when trying to use segues I encountered a few problems like the second view controller not showing up after the photo library dismissed or that the image was not showing up.

Segue example

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.destination is XferImageViewController {
        print("Test: ", pickedImage.image)
        let xferVC = segue.destination as? XferImageViewController
        xferVC?.storedImage = pickedImage.image
    }
    print("WHAT IS GOING ON")
//        if segue.destination is XferImageViewController {
//            let xferVC = segue.destination as? XferImageViewController
//            print(pickedImage.image)
//            //xferVC?.storedImage = pickedImage.image
//            xferVC?.storedImage = pickedImageVar
//        }

}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
        pickedImage.image = image
    } else {
        print("Something went wrong")
    }
    let image = info[UIImagePickerControllerOriginalImage] as? UIImage
    dismiss(animated:true, completion: nil)
    performSegue(withIdentifier: "xferImage", sender: self)
}

InstantiateViewController Example

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
        pickedImage.image = image
    } else {
        print("Something went wrong")
    }
    let image = info[UIImagePickerControllerOriginalImage] as? UIImage
    dismiss(animated:true, completion: nil)
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let controller = storyboard.instantiateViewController(withIdentifier: "xferImage") as! XferImageViewController
    controller.storedImage = image
    present(controller, animated: true, completion: nil)
}

So after using InstantiateViewController instead of segue I got the results that I wanted. What is the difference between the two? (I made sure that that segue identifier, tried segue.destination and storyboard ID but still wasn't getting what I needed) It is possible that I don't know how to use segue after the photo library dismiss call but still want to know the difference.

Upvotes: 4

Views: 789

Answers (2)

Rob
Rob

Reputation: 437592

The issue is that the segue is particular about the state of the view controller hierarchy when you initiate the segue. You have to defer the performSegue(withIdentifier:sender:) until the dismiss is done, namely put it into the completion handle of dismiss:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
        pickedImage.image = image
    } else {
        print("Something went wrong")
    }

    dismiss(animated: true) {
        self.performSegue(withIdentifier: "xferImage", sender: self)
    }
}

The above worked fine for me.

BTW, you can simplify your prepare(for:sender:) implementation:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let xferVC = segue.destination as? XferImageViewController {
        xferVC.storedImage = pickedImage.image
    }
}

Two somewhat unrelated observations:

  1. If pickedImage is an image view, I'd suggest renaming it (and updating the outlets in your storyboard) to pickedImageView. It's a good convention to avoid confusion between UIImage properties and UIImageView outlets.

  2. This is an even more minor observation, but in Model-View-Controller design, you generally don't want to rely on UIKit objects, like UIImageView to hold model objects, namely the selected image. It suggests a conceptual confusion between "view" objects and "model" objects. Plus, what if the current view controller didn't have a UIImageView?

    I'd personally suggest storing the selected image in a separate UIImage? property:

    private var selectedImage: UIImage?
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
            selectedImage = image
    
            // if you also want to update a `UIImageView` in the current
            // view controller, fine, do that, but it shouldn't be confused
            // with the "model".
            //
            // pickedImageView.image = image
        } else {
            print("Something went wrong")
        }
    
        dismiss(animated: true) {
            self.performSegue(withIdentifier: "xferImage", sender: self)
        }
    }
    

    And:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let xferVC = segue.destination as? XferImageViewController {
            xferVC.storedImage = selectedImage
        }
    }
    

Upvotes: 4

iOS Geek
iOS Geek

Reputation: 4855

Try using Function as Below

override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
    if segue.identifier == "segue"
    {
        if let xferVC = segue.destination as? XferImageViewController {
            xferVC.storedImage = pickedImage.image

                //Why Optional here ?
                //xferVC?.storedImage = pickedImage.image
            }
        }  
  }

Upvotes: 0

Related Questions