Kevin
Kevin

Reputation: 1189

Swift Appending Coordinates into Array and Adding to Map

My intentions of this app is to allow the user to input the any set of coordinates in one tab of a tab controller, and in another tab, the coordinates will be placed in a pin with an annotation.

The method I have been trying to implement is to use a global array that appends the input coordinates and called in the other class file (with the mapView). I have had a slew of problems in trying to iterate through the array to place the pins, and I am not sure what is wrong.

Input Info class file:

import UIKit
import CoreLocation

var locations: [Dictionary<String, Any>] = [] // here I initialize my array

class OtherVC: UIViewController {

    @IBOutlet weak var latitudeField: UITextField!
    @IBOutlet weak var longitudeField: UITextField!

    @IBOutlet weak var titleTextField: UITextField!
    var coordinates = [CLLocationCoordinate2D]()

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



 // This IBOutlet takes the coordinate input information from the user
 @IBAction func addToMap(_ sender: Any) {
    let lat = latitudeField.text!
    let long = longitudeField.text!
    let title = titleTextField.text!
    var location: [String: Any] = ["title": title, "latitude": lat, "longitude": long]
    locations.append(location) // adding the info to the array
    let mapVC : MapViewController = MapViewController()
    mapVC.iterateLocations()

    }


}

MapView class file:

import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate {


    @IBOutlet weak var mapView: MKMapView!



    override func viewDidLoad() {
        super.viewDidLoad()
        mapView.delegate = self
    }

    // this method here iterates through each of the locations in the array
    func iterateLocations() -> Bool {
        for location in locations {

            var momentaryLat = (location["latitude"] as! NSString).doubleValue
            var momentaryLong = (location["longitude"] as! NSString).doubleValue
            let annotation = MKPointAnnotation()
            annotation.title = location["title"] as? String
            annotation.coordinate = CLLocationCoordinate2D(latitude: momentaryLat as CLLocationDegrees, longitude: momentaryLong as CLLocationDegrees)
            mapView.addAnnotation(annotation)
        }
        return true
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let identifier = "pinAnnotation"
        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView

        if annotationView == nil {
             annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
             annotationView?.canShowCallout = true
        }

        annotationView?.annotation = annotation
        return annotationView
        }
}

The error pops up in the line "mapView.addAnnotation(annotation)" with it saying that a nil was found when trying to unwrap an Optional. So I think the error is that the information is not saving in annotation, but I don't immediately see where this is wrong.

Any alternatives for this method, that may be easier to implement, are welcome!

Upvotes: 0

Views: 1508

Answers (1)

stakri
stakri

Reputation: 1397

1) You are actually creating a new MapViewController, with local scope inside the addToMap method. This new view controller is created programmatically, and has nothing to do with the MapViewController instance of the UITabBarViewController. Its mapView outlet is set to nil so when you try to access that property, the implicit unwrapping will find nil, causing your app to crash. Also, the view controller local instance will be deallocated at the end of the addToMap method.

2) So, what you want to do is obtain access to the already existent MapViewController, through the UITabBarController. But again, if you try to access the mapView outlet before the user has switched to the map view tab at least once, you will see the same crash. This is because the MapViewController's view will not have been loaded (the view is loaded from the nib only when needed, i.e., in this case, when the user taps on the map view tab and that becomes visible). You could force the loading of the view, and setup of the mapView outlet by calling loadViewIfNeeded().

3) You are appending the new location to the previous ones, each time addToMap is invoked, and then you iterate over all locations to create annotations. This results to the same annotations being added to the map multiple times. You could change the iterateLocations method to something like add(newLocation:[String:Any]) and just pass the last location obtained from the user input.

So one solution would be to replace:

let mapVC : MapViewController = MapViewController() inside addToMap with:

let mapVC : MapViewController = self.tabBarController!.viewControllers![1] as! MapViewController
mapVC.loadViewIfNeeded()
mapVC.add(newLocation:location)

assuming that OtherVC is at index 0 and MapViewController is at index 1 of the tab view controller. Also:

func add(newLocation location:[String:Any]) {
        let momentaryLat = (location["latitude"] as! NSString).doubleValue
        let momentaryLong = (location["longitude"] as! NSString).doubleValue
        let annotation = MKPointAnnotation()
        annotation.title = location["title"] as? String
        annotation.coordinate = CLLocationCoordinate2D(latitude: momentaryLat as CLLocationDegrees, longitude: momentaryLong as CLLocationDegrees)
        mapView.addAnnotation(annotation)
}

Upvotes: 1

Related Questions