How to deal with "Unexpectedly found nil while unwrapping an Optional value"?

Hi all,

This is my code:



import UIKit


var imagePickerController:UIImagePickerController!

var secondViewController = SecondViewController()


@IBOutlet weak var camera1: UIButton!


@IBAction func takePhoto(_ sender: UIButton) {

// self.camera1.transform = CGAffineTransform(rotationAngle: CGFloat.pi)

if(UIImagePickerController.isSourceTypeAvailable(.camera))

{

self.imagePickerController = UIImagePickerController()

self.imagePickerController.delegate = self

self.imagePickerController.allowsEditing = true

self.imagePickerController.sourceType = UIImagePickerController.SourceType.camera

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

}

}

override func viewDidLoad() {

super.viewDidLoad()

}


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

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

secondViewController.infoFromViewOne = info[UIImagePickerController.InfoKey.editedImage] as? UIImage

self.dismiss(animated: true, completion: nil)

}


@objc func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {

self.dismiss(animated: true, completion: nil)

}

}



class SecondViewController: UIViewController {

var infoFromViewOne:UIImage?

@IBOutlet var image1: UIImageView!

override func viewDidLoad() {

super.viewDidLoad()

image1.image = infoFromViewOne

// Do any additional setup after loading the view.

}

}


I am trying to transmit a UIImage from another view when a button is clicked andI get an error saying: "fatal Error: Unexpectedly found nil while unwrapping an Optional value.


I really need help on this one as I have searched online for this topic and tried many things but nothing has worked. I would love to know if there is anything that can be done to solve this error or whether there is another war of changing a label from a different view.


All suggestions would be great!

Thanks

Accepted Reply

Thanks for confirmation. Considering the confirmed behavior this part may be causing the issue:

        self.present(secondViewController,animated: true,completion: nil)
        self.dismiss(animated: true, completion: nil)

You present the secondViewController without dismissing the UIImagePickerController, and dismiss it immediately after that.

Which causes Nothing happens.


Please try replacing the two lines with the following:

        self.dismiss(animated: true, completion: {
            self.present(secondViewController,animated: true,completion: nil)
        })


With this change, the code first dismisses the UIImagePickerController, and after dismissing completed, the secondViewController will be presented.

Replies

When I get Unexpectedly found nil, I check all the `!` in the code.

You have three, so you have plenty chance getting Unexpectedly found nil.


But the worst part of your code is here:

    var secondViewController = SecondViewController()


You instantiate your `SecondViewController` with so called default initializer.

This is one thing you should never do. The default initilizer of `UIViewController` is not clearly documented, but it will not connect IBOutlets.


So, your `image1` in `SecondViewController` is still nil, when `viewDidLoad()` is executed,

Thus, `image1.image = infoFromViewOne` causes Unexpectedly found nil.


Remove that line, add some code to instantiate the `SecondViewController` from storyboard, inside the method `imagePickerController(_:didFinishPickingMediaWithInfo:)`.

Thanks for your suggestion!

But when I insert this code to instantiate the `SecondViewController` from storyboard, I received something wrong:Cannot use instance member 'sb' within property initializer; property initializers run before 'self' is available


let sb = UIStoryboard(name:"Main",bundle: Bundle.main)

let secondViewController = sb.instantiateViewController(withIdentifier: "SecondViewController")


What should I do?

As I suggested, the two lines needs to be inside the method `imagePickerController(_:didFinishPickingMediaWithInfo:)`.

I just insert the 2 lines to this method:


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

let sb = UIStoryboard(name:"Main",bundle: Bundle.main)

let secondViewController = sb.instantiateViewController(withIdentifier: "secondViewController")

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

secondViewController.infoFromViewOne = info[UIImagePickerController.InfoKey.editedImage] as? UIImage

self.dismiss(animated: true, completion: nil)

}


and I got an error at line 5: Value of type 'UIViewController' has no member 'infoFromViewOne'


How can I dealc with it?

The return type of `instantiateViewController(withIdentifier:)` is declared as `UIViewController`. Even if it returns a specific subtype (in your case, `SecondViewController`) Swift compiler cannot have such knowledge.


You need explicit casting using `as!` (or `as?`).

    @objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        let sb = UIStoryboard(name:"Main",bundle: Bundle.main)
        let secondViewController = sb.instantiateViewController(withIdentifier: "secondViewController") as! SecondViewController
        secondViewController.infoFromViewOne = info[UIImagePickerController.InfoKey.editedImage] as? UIImage
        self.present(secondViewController,animated: true,completion: nil)
        self.dismiss(animated: true, completion: nil)
    }

With using `as!` as in line 03., you get more chance for your app to crash. But I usually choose `as!` rather than `as?` in this sort of code.

Crash here means you have inconsistent settings in your code and your storyboard. Crashing is a good way to tell you that you have such misconfiguration.

Thank you very much ! There's no error now.

But I find another problem: After I taking the photo by my phone and select the photo. It seems that it cannot go to the secondViewController but still remains in the initial page. As I want to transmit the photo to the secondViewController.


What happened to this ?

I do not understand what you mean by it cannot go to the secondViewController but still remains in the initial page.

The image chosen by the UIImagePicker is passed to the SecondViewController alse it resides in the initial page.


Please describe the observable fact, do not describe your own feeling with your own words.

I mean when I choose or take a picture, the program immediately goes to the secondViewController and displays the selected image on the secondViewController. Now that I have selected the picture, the program still stays on the starting page.




I'm sorry I didn't make it clear.


Maybe I need to repeat, please describe the behavior with observable facts.

What do you mean by the program still stays on the starting page.

You say displays the selected image on the secondViewController, I take it as you can observe the selected image in the SecondViewContoller, am I right? So, the program still stays on the starting page does not make sense.

I mean I don't want the page to stay on the original page, but to jump directly to secondViewController after choosing the photo. But now that I've selected the photos, the page is still in its original state, just like the page I saw when I started the program.


My English is not very good, I may not express clearly.

I'm not a native Englush speaker, so I may need more specific explanation.


But, you have neve described any operation that returns from the SecondViewController to the original view controller.

Why you say about the state of the original page.


Please describe only what you see and what you do, one by one.

I should describe the function of this program: when the user takes a picture or selects a picture, the program will recognize the content of the picture, and output the processed image with high resolution.



This program has two pages, the first page has two buttons, one is to take photos and the other is to select photos in albums. The second page will display the image selected by the user on the first page, process the image on the second page, and output the processed image.



Now the problem is that when I click the Photo Button, take a photo and click OK, the program still stays on the page where the user chooses to take a photo or select an album. And I hope that after identifying the photos, the program will go directly to the second page for processing, without the need for user manual operation.

Describing the functionality may help, but may not be enough.


The observable facts are...

- You see the first page

- You click the Phtoto button

- You take a picture and click OK in the UI shown by UIPickerView

- Nothing happens

Am I right?


If this is the right description, you should not have written something like the program immediately goes to the secondViewController and displays the selected image on the secondViewController. That is what you guessed and not an observable fact.

Your description is right.So I want to know how to do to achieve what I guess.

I just want to express what I hope but the application isn't going like this.


Thanks again.You're really patient.




To understand what's not going on, you could insert log info:


    @objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        let sb = UIStoryboard(name:"Main",bundle: Bundle.main)
print("shall instantiate")
        let secondViewController = sb.instantiateViewController(withIdentifier: "secondViewController")
print("did instantiate", secondViewController)
        self.present(secondViewController,animated: true,completion: nil)
print("presented")
        secondViewController.infoFromViewOne = info[UIImagePickerController.InfoKey.editedImage] as? UIImage
print("UIImage", secondViewController.infoFromViewOne)
        self.dismiss(animated: true, completion: nil)
    }

and tell exactly what you get