Does apple maps and MapKit use the same API? - ios

When I do a search in the Apple maps and one in my app using MapKit I get two different results for "New York".
Do they both use the same api?
If I do a search for "New York" in apple maps it will place the pointer in the center of NY city. But if I do a search for "New York" with MapKit using MKLocalSearchRequest and naturalLanguageQuery it will place the pointer a bit off, not even in Manhattan.
code:
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
localSearchRequest = MKLocalSearchRequest()
localSearchRequest.naturalLanguageQuery = "New York"
localSearch = MKLocalSearch(request: localSearchRequest)
localSearch.startWithCompletionHandler { (localSearchResponse, error) -> Void in
if localSearchResponse == nil || self.mapView == nil{
var alert = UIAlertView(title: "Not found", message: "Try again", delegate: self, cancelButtonTitle: "OK")
alert.show()
return
} else {
let location = CLLocationCoordinate2D(latitude: localSearchResponse.boundingRegion.center.latitude, longitude: localSearchResponse.boundingRegion.center.longitude)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
self.mapView.setRegion(region, animated: false)
self.pointAnnotation = MKPointAnnotation()
self.pointAnnotation.coordinate = location
self.mapView.addAnnotation(self.pointAnnotation)
}
}
}

they don't use the same api for displaying so I'd also assume they don't use the public API for searching.
Especially given the fact that the app came earlier than the API
BUT thats likely besides the point
You don't set the region property: "A map region that provides a hint as to where to search." (at least it isn't the same as in apple maps it seems)
for me it returns only ONE result as well [using mapkit]

Related

Problem with location permission during first time launch only in simulator iPhone6 - XCode 9.4.1

I have one weird problem.
The problem occurs only in iPhone6 simulator.
When i started app first time, without permission, then show fail in this code. This code located in ViewDidLoad in main ViewController
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.startUpdatingLocation()
var curLoc:CLLocation!
curLoc = manager.location
mapView.delegate = self
if (isLocationPermissionGranted() == false){
MapView.setRegion(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 52.406464, longitude: 16.924997), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)), animated: true)
}else
{
MapView.setRegion(MKCoordinateRegionMake(CLLocationCoordinate2DMake(curLoc.coordinate.latitude, curLoc.coordinate.longitude), MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)), animated: true)
}
let getJSON = JSONDownload()
getJSON.JSONDownloader(MapView: MapView)
}
In else block I have error
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an
Optional value
But in any other simulator or my phone(iPhone 6s, iOS 11.4.1) only show
Could not inset legal attribution from corner 4
About this message I'm little confused too, because, I suppose, I have every permission option.
My permission stuff is:
in Info.plist
Privacy - Location When In Use Usage Description
Privacy - Location Usage Description
In code in ViewController
let manager = CLLocationManager()
In addition, I have protection against localized errors:
func isLocationPermissionGranted() -> Bool{
guard CLLocationManager.locationServicesEnabled() else{
return false
}
return [.authorizedAlways, .authorizedWhenInUse].contains(CLLocationManager.authorizationStatus())
}
Is any chance to fix it? :)
Thanks for answer! :)
In your code you are declaring curLoc as an implicitly unwrapped optional and then assigning manager.location to it; but manager.location is an optional and may be nil. There are a number of reasons that location could be nil; It takes time for a device to determine its location or the user may have denied location access.
Whatever the reason, when you subsequently access curLoc when it contains nil you get an exception because the contract of an implicitly unwrapped optional is that it won't be nil.
You need to unwrap manager.location safely in order to avoid a crash.
mapView.delegate = self
if let curLoc = manager.location, isLocationPermissionGranted() {
MapView.setRegion(MKCoordinateRegionMake(CLLocationCoordinate2DMake(curLoc.coordinate.latitude, curLoc.coordinate.longitude), MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)), animated: true)
} else {
MapView.setRegion(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 52.406464, longitude: 16.924997), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)), animated: true)
}

Get back to a view with the same directions that existed before

well, guess this one is kinda big...
I'm making an app in which there will be a MapKit and a button. When the user clicks the button, he/she will open a new ViewController with several options of destinations to go. After confirming the one he wants to go, the MapKit view will reopen with directions for the specific place. To do this, I created a function on the ViewController linked to the MapKit view:
func createmap(latit: Double, longit: Double){
//set what's going to show up in the Map
MapView.delegate = self
MapView.showsScale = true
MapView.showsPointsOfInterest = true
MapView.showsUserLocation = true
//request authorization for user location data storage
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
//if authorization is given, use the user location
if CLLocationManager.locationServicesEnabled(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
//set source and destination coordinates of direction recommendation
let sourceCoordinates = locationManager.location?.coordinate
let destinationCoords = CLLocationCoordinate2D(latitude: latit, longitude: longit)
//set placemarks with source and destination coordinates
let sourcePlacemark = MKPlacemark(coordinate: sourceCoordinates!)
let destPlacemark = MKPlacemark(coordinate: destinationCoords)
//put placemarks on maps with the source and destination coordinates
let sourceItem = MKMapItem(placemark: sourcePlacemark)
let destItem = MKMapItem(placemark: destPlacemark)
//set direction request, source and destination request coordinates and transport type
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceItem
directionRequest.destination = destItem
directionRequest.transportType = .automobile
//set response if sucess or error
let directions = MKDirections(request: directionRequest)
directions.calculate(completionHandler: {
response, error in
guard let response = response else {
if let error = error {
print("Something went wrong.")
}
return
}
//set format of route line
let route = response.routes[0]
self.MapView.add(route.polyline, level: .aboveRoads)
//set map framing
let rekt = route.polyline.boundingMapRect
self.MapView.setRegion(MKCoordinateRegionForMapRect(rekt), animated: true)
})
}
And then, to make this function run, I did this:
override func viewDidLoad() {
super.viewDidLoad()
createmap(latit: lat, longit: long)
}
lat and long are variables declared publicly.
In the other view, I have a button called "go back to the Map" in which I try to make a simple segue. However, there is a problem with this code: if I get directions to some place, reopen the other view and then press "go back to the Map", the directions that were set disappear. I don't know what to do to make them stay, can someone please help?
just a bit more information: the "go back to the Map" button is linked to the MapKit view as a simple segue (i clicked, dragged and dropped to the MapKit view, didn't write any line of code)
I think problem is your Map VC is already added into the navigation stack so ViewDidLoad method is not called again you should move your code from viewdidload to viewWill appear like
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
createmap(latit: lat, longit: long)
}
Did you used this Function to add a polyline? you added poly line correctly . but I think you had not provided values to poly line for drawing path
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 4.0
return renderer
}

Zoom in on user location in iOS

I've been trying to solve this for a few hours and I can't seem to arrive at a solution. I am trying to create a button on my map view that zooms in on the users location when pressed. Here is the code for the function that pertains to the button:
func zoomInOnLocation() {
let userLocation = MKUserLocation()
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
let currentLocation: CLLocation? = userLocation.location
let latitude = currentLocation?.coordinate.latitude
let longitude = currentLocation?.coordinate.longitude
let span: MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude!, longitude!)
let region: MKCoordinateRegion = MKCoordinateRegionMake(location, span)
mapView.setRegion(region, animated: true)
}
When I click the button in the simulator, I receive an error stating fatal error:
unexpectedly found nil while unwrapping an Optional value
with the fifth line mapDelegate.mapView!... highlighted in red. Also, I added the proper tag to Info.plist. Any help is much appreciated.
Check this :
if #available(iOS 9.0, *) {
locationManager.requestLocation()
} else {
// Fallback
}
let latitude:CLLocationDegrees = //insert latitutde
let longitude:CLLocationDegrees = //insert longitude
let latDelta:CLLocationDegrees = 0.05
let lonDelta:CLLocationDegrees = 0.05
let span = MKCoordinateSpanMake(latDelta, lonDelta)
let location = CLLocationCoordinate2DMake(latitude, longitude)
let region = MKCoordinateRegionMake(location, span)
mapView.setRegion(region, animated: false)
For more : Making the map zoom to user location and annotation (swift 2)
So you probably do not want to to interact with the mapView delegate as you are doing right now.
How about adding the delegates to the class of the view that holds the mapView, like this:
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
and setting the delegates in viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
mapView.delegate = self
}
And of course you want to have the locationManager and userLocation set up:
let locationManager = CLLocationManager()
var userLocation = CLLocation()
Notice that userLocation is a variable because most likely you would want to update it at some point.
This is how you probably want to work with locationManager:
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
You would make changes according to the needs of your app, and taking into account that the desired accuracy has an impact on battery life. Also, are you starting and stopping updates on userLocation? Because I do not see that in your code, unless you are doing it outside of this function.
A good practice is to try to minimize what an specific function does down to one task. You probably want to do all this setup elsewhere and then only zoom in inside the function. :)
Finally, in order to zoom in, you can change the values of MKCoordinateSpanMake, and remember that larger span values zoom in the map, so a smaller area is viewable.
let userLocationCoordinates = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
let span = MKCoordinateSpanMake(0.3, 0.3)
let region = MKCoordinateRegion(center: userLocationCoordinates, span: span)
mapView.setRegion(region, animated: true)
Hopefully that helps you out a bit, let me know how it goes!
Perhaps this will help. I created this function to zoom into an area defined by an array of positions, ranging from just the current user location out to the area around a set of points making up a polyline. The function provides for a buffer around the points based on a regionPaddingFactor constant set in my system constants.
func setRectView(_ locations: [MKAnnotation], mapView: MKMapView) // Size the area for display and reset the view
{
var maxLat = -90.0
var minLat = 90.0
var maxLon = -180.0
var minLon = 180.0
if locations.count >= 1 {
for waypoint in locations {
maxLat = max(maxLat, waypoint.coordinate.latitude)
minLat = min(minLat, waypoint.coordinate.latitude)
maxLon = max(maxLon, waypoint.coordinate.longitude)
minLon = min(minLon, waypoint.coordinate.longitude)
}
let loc = CLLocationCoordinate2DMake((maxLat-fabs(maxLat - minLat)/2), (maxLon-fabs(maxLon - minLon)/2))
let span = MKCoordinateSpanMake(0.001 + (1.0 + Setting.shared.regionPaddingFactor) * fabs(maxLat - minLat), 0.001 + (1.0 + Setting.shared.regionPaddingFactor) * fabs(maxLon-minLon))
// The 0.001 values above ensure that you do not get a 0.0 valued span if all of the points have the same latitude, longitude, or both, or if there is only one point
// The regionPaddingFactor is a constant to allow some space around the points passed in
let reg = MKCoordinateRegionMake(loc, span)
mapView.setRegion(reg, animated: true)
mapView.animatedZoom(zoomRegion: reg, duration: 0.8)
}
}
In the calling code, I provide for 3 settings that rotate as the user presses the button:
Show a tight view (just pass current location in the call)
Show the whole route (pass all points in the template)
Change to manual zooming / positioning
The last option is needed since I call the function whenever a new current position is received to reposition the view based on current location, which repeatedly refocuses the view if the user is trying to reposition the map.
If you don't want the flexibility of sending different position arrays, you can do the positioning here using mapView.annotations or just the current location as the array.

how to send a request to google direction api to get a path in swift

I'm new to swift and
I'm creating an App that will show the location of the user and put a marker to that position. After the user moves. The marker would be removed and a new marker would be created. Now. I want to make markers on Point A and Point B into the app and show the route on the map. It shall use the nearest road on the map.
i already study the google map document but i need help i can't understand how to make a route between 2 points?
I would be happy if you help me, thank-you very much.
Based from this blog, you need first to add the property let locationManager = CLLocationManager() that will add and instantiate a CLLocationManager property named locationManager.
Next, find viewDidLoad() and add these two lines to the bottom that will make MapViewController the delegate of locationManager and request access to the user’s location.
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
From this related thread, you have to instantiate the CLLocationManager class in viewDidLoad() like this:
// Ask for Authorisation from the User.
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
Then in CLLocationManagerDelegate method you can get user's current location coordinates:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var locValue:CLLocationCoordinate2D = manager.location.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
Then to add a marker, create a GMSMarker object that includes a position and title, and set its map. The following example demonstrates how to add a marker to an existing GMSMapView object. The marker is created at coordinates 10,10, and displays the string "Hello world" in an info window when clicked.
let position = CLLocationCoordinate2DMake(10, 10)
let marker = GMSMarker(position: position)
marker.title = "Hello World"
marker.map = mapView
Lastly, make a route between 2 points, you can check these links:
How to get route between two locations in iOS?
Now in createRoute method give that city name or what ever you want
as origin like this:
#IBAction func createRoute(sender: AnyObject) {
let addressAlert = UIAlertController(title: "Create Route", message: "Connect locations with a route:", preferredStyle:
UIAlertControllerStyle.Alert)
addressAlert.addTextFieldWithConfigurationHandler { (textField) -> Void in
//give a origin for route
textField.text = self.currentLocationName
textField.userInteractionEnabled = false
}
addressAlert.addTextFieldWithConfigurationHandler { (textField) -> Void in
textField.placeholder = "Destination?"
}
let createRouteAction = UIAlertAction(title: "Create Route", style: UIAlertActionStyle.Default) { (alertAction) -> Void in
let origin = (addressAlert.textFields![0] as! UITextField).text as String
let destination = (addressAlert.textFields![1] as! UITextField).text as String
self.mapTasks.getDirections(origin, destination: destination, waypoints: nil, travelMode: nil, completionHandler: {
(status, success) -> Void in
if success {
self.configureMapAndMarkersForRoute()
self.drawRoute()
self.displayRouteInfo()
}
else {
println(status)
}
})
}
let closeAction = UIAlertAction(title: "Close", style: UIAlertActionStyle.Cancel) { (alertAction) -> Void in
}
addressAlert.addAction(createRouteAction)
addressAlert.addAction(closeAction)
presentViewController(addressAlert, animated: true, completion: nil)
}
Drawing Route Between Two Places on GMSMapView in iOS
First, get all points coordinates which are coming in route then add
these points latitude and longitude in path in will draw path
according to that
GMSCameraPosition *cameraPosition=[GMSCameraPosition cameraWithLatitude:18.5203 longitude:73.8567 zoom:12];
_mapView =[GMSMapView mapWithFrame:CGRectZero camera:cameraPosition];
_mapView.myLocationEnabled=YES;
GMSMarker *marker=[[GMSMarker alloc]init];
marker.position=CLLocationCoordinate2DMake(18.5203, 73.8567);
marker.icon=[UIImage imageNamed:#"aaa.png"] ;
marker.groundAnchor=CGPointMake(0.5,0.5);
marker.map=_mapView;
GMSMutablePath *path = [GMSMutablePath path];
[path addCoordinate:CLLocationCoordinate2DMake(#(18.520).doubleValue,#(73.856).doubleValue)];
[path addCoordinate:CLLocationCoordinate2DMake(#(16.7).doubleValue,#(73.8567).doubleValue)];
GMSPolyline *rectangle = [GMSPolyline polylineWithPath:path];
rectangle.strokeWidth = 2.f;
rectangle.map = _mapView;
self.view=_mapView;
Hope this helps!

MapKit Not Showing Location

I've implemented MapKit into my Swift app and everything runs properly and when I run the app it shows a map. However, it's an overview of the United States and not where I want it to be.
I've imported the MapKit into ViewController and connected the MapView to the ViewController via an IBOutlet, however it's not working.
I didn't put the code in the viewDidLoad because the app crashed every time. Rather, I made a function and that seemed to do the trick. This is all of the code that I've got going for this map:
func mapMexico()
{
let lat:CLLocationDegrees = 20.648097
let long:CLLocationDegrees = -105.235168
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
let latDelta:CLLocationDegrees = 0.01
let longDelta:CLLocationDegrees = 0.01
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: longDelta)
let region = MKCoordinateRegion(center: coordinate, span: span)
mapView.setRegion(region, animated: true)
}

Resources