0

I am receiving a warning: "Warning: Attempt to present on whose view is not in the window hierarchy!"

I think I have a logic problem related to the presentation of the new view controller via a segue. I am attempting to geocode a location and assign the coordinates to variables in a struct (along with a couple of other variables) that I then send to a new controller which displays a pin on a mapView.

Via breakpoints and print statements, it appears that my prepareForSegue function is called twice, once before the geolocation actually occurs and after the next view controller is called, and once after the geolocation has completed... but I believe it can't display the geocoded location because the view controller already exists... I always end up in the middle of the ocean somewhere.

I have tried moving my async function (performUIUpdatesOnMain) around and moving my map and annotation display functionality into viewDidAppear in the second view controller, but these solutions don't work. What am I doing wrong?

Here are the relevant parts of each view controller:

First view controller:

@IBAction func findLocationPressed(_ sender: Any) {

    // Show alert if locationTextField or websiteTextField are empty
    if self.locationTextField.text == "" || self.websiteTextField.text == "" {

        let alertController = UIAlertController()
        let alert = UIAlertAction(title: "You didn't enter a location or website", style: .cancel, handler: nil)
        alertController.addAction(alert)
        present(alertController, animated: true, completion: nil)

    } else {

        // Populate locationData struct with locationText and mediaURL
        locationData.locationText = self.locationTextField.text!
        locationData.mediaURL = self.websiteTextField.text!

        // Get the location
        getLocation(completionHandler: { (success, error) in

            //AlertView.activityIndicator

            //performUIUpdatesOnMain {

            // If geocoding successful...
            if success {

                // Present the ConfirmLocationViewController

                self.performSegue(withIdentifier: "FinishSegue", sender: self)
                /*let controller = self.storyboard?.instantiateViewController(withIdentifier: "ConfirmLocationViewController") as! ConfirmLocationViewController
                self.present(controller, animated: true, completion: nil) */
            } else {

                let alertController = UIAlertController()
                let alert = UIAlertAction(title: "Couldn't find that location", style: .cancel, handler: nil)
                alertController.addAction(alert)
                self.present(alertController, animated: true, completion: nil)
            //}
            }
        })
    }
}

func getLocation(completionHandler: @escaping (_ success: Bool, _ error: NSError?) -> Void) {

    // Create an instance of the geocoder
    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(locationTextField.text!) { (placemark, error) in

        performUIUpdatesOnMain {

        // Check for an error when retrieving the coordinates
        if error != nil {
            let userInfo = [NSLocalizedDescriptionKey: "There was an error attempting to retrieve your coordinates"]
            completionHandler(false, NSError(domain: "getLocation", code: 0, userInfo: userInfo))

        } else {

            // GUARD: Confirm placemark exists
            guard let placemark = placemark else {
                let userInfo = [NSLocalizedDescriptionKey: "There was an error with your placemark"]
                completionHandler(false, NSError(domain: "getLocation", code: 1, userInfo: userInfo))
                return
            }

            // GUARD: Confirm latitude exists in placemark
            guard let latitude = placemark[0].location?.coordinate.latitude else {
                let userInfo = [NSLocalizedDescriptionKey: "There was an error with the latitude"]
                completionHandler(false, NSError(domain: "getLocation", code: 2, userInfo: userInfo))
                return
            }

            // GUARD: Confirm longitude exists in placemark
            guard let longitude = placemark[0].location?.coordinate.longitude else {
                let userInfo = [NSLocalizedDescriptionKey: "There was an error with the longitude"]
                completionHandler(false, NSError(domain: "getLocation", code: 3, userInfo: userInfo))
                return
            }

            // Populate locationData with latitude and longitude
            self.locationData.latitude = latitude
            self.locationData.longitude = longitude

            completionHandler(true, nil)
            print("getLocation was successful. \n Latitude=\(latitude) \n Longitude=\(longitude)")
            }
        }
    }
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "FinishSegue" {
        let controller = segue.destination as! ConfirmLocationViewController
        controller.locationData = self.locationData
        print("This is the locationData being sent via prepareForSegue: \(locationData)")
    }
}

Second view controller:

   override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)

    print("viewWillAppear in ConfirmLocationViewController has been called")

    // Set the coordinates
    let coordinates = CLLocationCoordinate2D(latitude: locationData.latitude, longitude: locationData.longitude)
    print(coordinates)

    // Set the map region
    let region = MKCoordinateRegionMake(coordinates, MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
    self.mapView.setRegion(region, animated: true)
    self.mapView.delegate = self

    // Set the annotation
    let title = "\((User.shared.firstName) + " " + (User.shared.lastName))"
    let subtitle = locationData.mediaURL
    annotation.coordinate = coordinates
    annotation.title = title
    annotation.subtitle = subtitle

    // Add the annotation
    mapView.addAnnotation(self.annotation)
    self.mapView.addAnnotation(self.annotation)

    print("the current map region is: \(region)")

    /*performUIUpdatesOnMain {

    } */
}

The struct I am passing, called locationData contains four variables: latitude, longitude, and two strings.

Thanks.

Pigpocket
  • 449
  • 5
  • 24
  • 2
    If you are performing your segue programatically then you need to connect the segue from your view controller to the destination view controller, not from the button. – dan Oct 19 '17 at 22:07
  • @dan That was it! Man I spent hours re-engineering my code to make that work. Thanks! – Pigpocket Oct 19 '17 at 22:18

0 Answers0