iOS 10 NSUserActivity, Location Suggestions - MKMapItem Does not Appear in Maps - ios

I am trying to make place suggestions appear in Apple Maps in the list under "Where do you want to go?". NSUserActivity in iOS 10 now has a mapItem property and I'm setting it with an MKMapItem that I create from an MKPlacemark that I made with geo coordinates and the place name.
The place name does not appear when I go to Maps, as it should. After going through WWDC 2016, session 240 several times, I still cannot find what I'm doing wrong.

The mapItem must be from an MKLocalSearch request that takes your geo coords and place name. The mapItems that you get from the MKLocalSearchResponse are ones that Apple Maps will accept.
let coordinate = CLLocationCoordinate2D(latitude: 38.89005200, longitude: -77.00251600)
var points = [MKMapPointForCoordinate(coordinate)]
let mapRect = MKPolygon(points: &points, count: 1).boundingMapRect
let region = MKCoordinateRegionForMapRect(mapRect)
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "Supreme Court Historical Society"
request.region = region
let localSearch:MKLocalSearch = MKLocalSearch(request: request)
localSearch.start(completionHandler: { (response:MKLocalSearchResponse?, error:Error?) in
if error == nil {
activity.mapItem = response!.mapItems[0]
var userInfo = [String: AnyObject]()
userInfo["placemark"] = NSKeyedArchiver.archivedData(withRootObject: activity.mapItem.placemark)
activity.userInfo = userInfo
activity.contentAttributeSet?.supportsNavigation = true
activity.contentAttributeSet?.supportsPhoneCall = true
}
})

Related

Using Firebase data and create Google Maps API markers on iOS

I am trying to make an iOS app, working with Google Maps API and Firebase. It should show many markers on the map.
The maps part works well. However, I want to retrieve location data (latitude, longitude) from Firebase Real-time database, then put into marker.position. (then it should create a marker, right?)
It struck many days, I still can't show a marker (but I can print correct data from Firebase database).
ref = Database.database().reference()
ref.child("locations").observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
if let location = snapshot.value as? [String:Any] {
for eachLocation in location {
print("Location: \(eachLocation)")
if let locationCoordinate = eachLocation.value as? [String: Any] {
if let lavLatitude = locationCoordinate["latitude"] as? Double {
if let lavLongitude = locationCoordinate["longitude"] as? Double {
print(lavLatitude)
print(lavLongitude)
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(lavLatitude, lavLongitude)
marker.map = mapView
}
}
}
}
}
}
}
My code reference from Github: appsmall/Map-Demo.
It really weird, when I copy and replace my GoogleService-Info.plist. My app can show markers from his Firebase database!
I am wondering anything wrong in my database: Screenshot.
Of course, I searched and tried many different solutions here, but still not work, if you need more information, please tell me, thanks!
Try using Annotation instead of GMSMarker():
Replace this
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(lavLatitude, lavLongitude)
marker.map = mapView
With this
let position = CLLocationCoordinate2D(latitude: lavLatitude, longitude: lavLongitude)
let marker = GMSMarker(position: position)
marker.title = "Hello World"
marker.map = mapView

How to calculate distance between two locations using google api in swift 4

I am currently working on an IOS project in which i have to calculate distance between two location. I have done without using google but i want to use google api to get accurate distance i am sharing my code here
let myLocation = CLLocation(latitude: CLLocationDegrees(latittude[indexPath.row])!, longitude: CLLocationDegrees(longittude[indexPath.row])!)
let lat = UserDefaults.standard.string(forKey: "lat") ?? ""
let long = UserDefaults.standard.string(forKey: "long") ?? ""
let myBuddysLocation = CLLocation(latitude: CLLocationDegrees(lat)!, longitude: CLLocationDegrees(long)!)
Use distance function of CoreLocation Framework,
var startLocation = CLLocation(latitude: startLatitude, longitude: startLongitude)
var endLocation = CLLocation(latitude: endLatitude, longitude: endLongitude)
var distance: CLLocationDistance = startLocation.distance(from: endLocation)
Swift 5+:
As far as I know, there are two ways to find the distance.
If you are looking for driving distance, you can always use MKDirections.
Here is the code for finding driving distance (You can also find walking, and transit distance by changing transport type).
let sourceP = CLLocationCoordinate2DMake( sourceLat, sourceLong)
let destP = CLLocationCoordinate2DMake( desLat, desLong)
let source = MKPlacemark(coordinate: sourceP)
let destination = MKPlacemark(coordinate: destP)
let request = MKDirections.Request()
request.source = MKMapItem(placemark: source)
request.destination = MKMapItem(placemark: destination)
// Specify the transportation type
request.transportType = MKDirectionsTransportType.automobile;
// If you want only the shortest route, set this to a false
request.requestsAlternateRoutes = true
let directions = MKDirections(request: request)
// Now we have the routes, we can calculate the distance using
directions.calculate { (response, error) in
if let response = response, let route = response.routes.first {
print(route.distance) //This will return distance in meters
}
}
If you are only looking for air distance/bird's eye distance/coordinate distance, you can use this code:
let sourceP = CLLocation(latitude: sourceLat, longitude: sourceLong)
let desP = CLLocation(latitude: desLat, longitude: desLong))
let distanceInMeter = sourceP.distance(from: desP)

Calculate distance between my location and a MapKit pin on Swift

I need your help, I'm working on an App where I have some pins (locations) and what I want is to get the distance between each one and my location. My code is the following
let annotation = MKPointAnnotation()
let annotationTwo = MKPointAnnotation()
let saintPaulHospitalBC = MKPointAnnotation()
override func viewDidLoad() {
super.viewDidLoad()
mapita.showsUserLocation = true // Mapita is the name of the MapView.
annotation.coordinate = CLLocationCoordinate2D(latitude: 25.647399800, longitude: -100.334304500)
mapita.addAnnotation(annotation)
annotationTwo.coordinate = CLLocationCoordinate2D(latitude: 25.589339000, longitude: -100.257724800)
mapita.addAnnotation(annotationTwo)
saintPaulHospitalBC.coordinate = CLLocationCoordinate2D(latitude: 49.280524700, longitude: -123.128232600)
mapita.addAnnotation(SaintPaulHospitalBC)
}
When I run the code, the map shows the pins, but what else can I do to start calculating the distance? Thank you!
You're gonna have to convert the coordinates of your annotations to CLLocation types, then get the distance between them. To ignore the height of the coordinates, as they are 2D, just use the latitude and longitude properties of the 2D coordinates, like so:
let loc1 = CLLocation(latitude: coord1.latitude, longitude: coord1.longitude)
However, CLLocation has some other properties such as speed and height, so if you want to factor those in you'll have to give more information. To find the distance between the two locations, do this:
let distance = loc1.distance(from: loc2)
This will give your answer as a double in meters.
Create a helper function to compute the distance between the user location and a given MKPointAnnotation pin:
/// Returns the distance (in meters) from the
/// user's location to the specified point.
private func userDistance(from point: MKPointAnnotation) -> Double? {
guard let userLocation = mapita.userLocation.location else {
return nil // User location unknown!
}
let pointLocation = CLLocation(
latitude: point.coordinate.latitude,
longitude: point.coordinate.longitude
)
return userLocation.distance(from: pointLocation)
}
Finally, to get the user distance to Saint Paul hospital:
if let distance = userDistance(from: saintPaulHospitalBC) {
// Use distance here...
}
Geolocation tracking latency. There is a catch though: the user distance might not always be available at first, since MapKit/CoreLocation geolocation tracking might still be running in the background.
One way around this, is to conform to the MKMapViewDelegate protocol and wait for the mapView(_:didUpdate:) callback before finally computing your distances.
To put it in perspective, you need to first specify what "distance" are you looking for. If you are looking for simple Euclidean Distance then any of the other answers or using distanceFromLocation would work. According to Apple's documentaion on distanceFromLocation
This method measures the distance between the two locations by tracing
a line between them that follows the curvature of the Earth. The
resulting arc is a smooth curve and does not take into account
specific altitude changes between the two locations.
This means, that the distance derived using this method will not be the actual route/transportation distance between two points.
If that is what you are looking for then head over to the answer I linked above, if not then keep reading (but either way, I encourage you to read the whole post :).
If you are looking for "route" distance (drivable, walkable etc.) between your location and the other annotations in the map, it's going to take little more work using MKRoute object. To be more specific you need to first have access to the MKMapItem objects of each of your annotations and then a custom method like below would be able to get the route info between two MapItem objects.
Note - if you don't have MapItems then you can create them just using the coordinates of each of your annotations, like so
ley myCoordinates CLLocationCoordinate2D(latitude: 25.647399800, longitude: -100.334304500)
let myPlacemark = MKPlacemark(coordinate: myCoordinates)
let myMapItem = MKMapItem(placemark: myPlacemark)
Define an MKRoute variable globally in your class (or ViewController class). This var will hold the calculated Route information between two points.
var route: MKRoute!
and then
func getDistanceToDestination(srcMapItem srcmapItem: MKMapItem, destMapItem destmapItem: MKMapItem){
let request = MKDirectionsRequest() //create a direction request object
request.source = srcmapItem //this is the source location mapItem object
request.destination = destmapItem //this is the destination location mapItem object
request.transportType = MKDirectionsTransportType.automobile //define the transportation method
let directions = MKDirections(request: request) //request directions
directions.calculate { (response, error) in
guard let response = response else {
print(error.debugDescription)
return
}
self.route = response.routes[0] //get the routes, could be multiple routes in the routes[] array but usually [0] is the best route
}
}
Usage would be
self.getDistanceToDestination(srcMapItem: yourSourceMapItemObj, destMapItem: yourDestinationMapitemObj)
where yourSourceMapItemObj and yourDestinationMapitemObj are two MapItem objects aka source and destination points.
And then you can access the distance using self.route.distance to get the distance of the first best route returned by MKRoute. There are a whole bunch of other properties for the MKRoute object route which you can use as well to display/calculate other things, and I encourage you to take a look at those too. You can use the function above to also draw a ployLine i.e. a line showing the route between the two locations in the MapView just by adding self.mapView.add(self.route.polyline) in the end of the custom method above and then use the below MKMapViewDelegate function below to render the polyline.
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let linerenderer = MKPolylineRenderer(overlay: self.route.polyline)
linerenderer.strokeColor = .blue
linerenderer.lineWidth = 3.5
return linerenderer
}
And finally, make sure your class (or your class extension) complies to CLLocationManagerDelegate and MKMapViewDelegate protocols and mapview delegate pointed to self (which I assume you already do) in order for everything above to work.
Its easy try my code below.
Don't forget to import CoreLocation or MapKit, hope it helps you
func calculateDistancefrom(sourceLocation: MKMapItem, destinationLocation: MKMapItem, doneSearching: #escaping (_ expectedTravelTim: TimeInterval) -> Void) {
let request: MKDirectionsRequest = MKDirectionsRequest()
request.source = sourceLocation
request.destination = destinationLocation
request.requestsAlternateRoutes = true
request.transportType = .automobile
let directions = MKDirections(request: request)
directions.calculate { (directions, error) in
if var routeResponse = directions?.routes {
routeResponse.sort(by: {$0.expectedTravelTime <
$1.expectedTravelTime})
let quickestRouteForSegment: MKRoute = routeResponse[0]
doneSearching(quickestRouteForSegment.distance)
}
}
}
func getDistance(lat: Double, lon: Double, completionHandler: #escaping (_ distance: Int) -> Void) {
let destinationItem = MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2DMake(lat, lon)))
guard let currentLocation = self.locationManager?.location else { return }
let sourceItem = MKMapItem(placemark: MKPlacemark(coordinate: currentLocation.coordinate))
self.calculateDistancefrom(sourceLocation: sourceItem, destinationLocation: destinationItem, doneSearching: { distance in
completionHandler(distance)
})
}
//Thereafter get the distance in meters by calling
self.getDistance(lat: yourLat, lon: YourLon) { distance in
}
//you can divide by 1000 to convert to KM... .etc
Using MapKit & Swift 5
Calculate distance between two location location
Sample Function : I have tested in Google Map as well as Apple Map
let startLocation : CLLocation = CLLocation.init(latitude: 23.0952779, longitude: 72.5274129)
let endLocation : CLLocation = CLLocation.init(latitude: 23.0981711, longitude: 72.5294229)
let distance = startLocation.distance(from: endLocation)
self.getDistance(departureDate: Date().adjust(hour: 8, minute: 0, second: 0, day: 0, month: 0), arrivalDate: Date().adjust(hour: 8, minute: 10, second: 0, day: 0, month: 0), startLocation: startLocation, endLocation: endLocation) { (distanceInMeters) in
print("fake distance: \(distance)")
let fakedistanceInMeter = Measurement(value: distance, unit: UnitLength.meters)
let fakedistanceInKM = fakedistanceInMeter.converted(to: UnitLength.kilometers).value
let fakedistanceInMiles = fakedistanceInMeter.converted(to: UnitLength.miles).value
print("fakedistanceInKM :\(fakedistanceInKM)")
print("fakedistanceInMiles :\(fakedistanceInMiles)")
print("actualDistance : \(distanceInMeters)")
let distanceInMeter = Measurement(value: distanceInMeters, unit: UnitLength.meters)
let distanceInKM = distanceInMeter.converted(to: UnitLength.kilometers).value
let distanceInMiles = distanceInMeter.converted(to: UnitLength.miles).value
print("distanceInKM :\(distanceInKM)")
print("distanceInMiles :\(distanceInMiles)")
}
Use of functions
self.getDistance(departureDate: trip.departure.dateTime, arrivalDate: trip.arrival.dateTime, startLocation: startLocation, endLocation: endLocation) { (actualDistance) in
print("actualDistance : \(actualDistance)")
}
I am improved above function and added code here, I hope it will help someone.
func calculateDistancefrom(departureDate: Date, arrivalDate: Date, sourceLocation: MKMapItem, destinationLocation: MKMapItem, doneSearching: #escaping (_ distance: CLLocationDistance) -> Void) {
let request: MKDirections.Request = MKDirections.Request()
request.departureDate = departureDate
request.arrivalDate = arrivalDate
request.source = sourceLocation
request.destination = destinationLocation
request.requestsAlternateRoutes = true
request.transportType = .automobile
let directions = MKDirections(request: request)
directions.calculate { (directions, error) in
if var routeResponse = directions?.routes {
routeResponse.sort(by: {$0.expectedTravelTime <
$1.expectedTravelTime})
let quickestRouteForSegment: MKRoute = routeResponse[0]
doneSearching(quickestRouteForSegment.distance)
}
}
}
func getDistance(departureDate: Date, arrivalDate: Date, startLocation : CLLocation, endLocation : CLLocation, completionHandler: #escaping (_ distance: CLLocationDistance) -> Void) {
let destinationItem = MKMapItem(placemark: MKPlacemark(coordinate: startLocation.coordinate))
let sourceItem = MKMapItem(placemark: MKPlacemark(coordinate: endLocation.coordinate))
self.calculateDistancefrom(departureDate: departureDate, arrivalDate: arrivalDate, sourceLocation: sourceItem, destinationLocation: destinationItem, doneSearching: { distance in
completionHandler(distance)
})
}

Open directions func not opening apple maps programmatically

I am working on a project right now and am stuck on the final part. I have designed an app with 20 custom annotations to be used by our wedding guests to find nearby restaurants, bars, coffee shops, etc. There are various custom annotation to identify what each location is. It also asks for users current location so they can find things nearby. I would like to be able to click on the annotations and have our friends and family be able to get directions from their current location to the annotation. It seems like the best way to do this is by opening up the apple maps app with my latitude and longitude coordinates in my custom point annotations. My code though does not seem to open that maps app at all. I have added a right call out, I have looked through every since question old and new asking about this and can not find a solution. Any advice will be much appreciated as this is my first real swift app. Below is my current open directions function. Thank you in advance for any help or insight.
func openDirections(_ address :String?) {
self.geocoder.geocodeAddressString(address!, completionHandler: { (placemarks :[CLPlacemark]?, error :NSError?) -> Void in
let placemark = placemarks![0] as CLPlacemark
let destinationPlacemark = MKPlacemark(coordinate: placemark.location!.coordinate, addressDictionary: placemark.addressDictionary as? [String:NSObject])
let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: placemark.location!.coordinate, addressDictionary:nil))
mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving])
let startingMapItem = MKMapItem.forCurrentLocation()
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
let directionsRequest = MKDirectionsRequest()
directionsRequest.transportType = .automobile
directionsRequest.source = startingMapItem
directionsRequest.destination = destinationMapItem
let directions = MKDirections(request: directionsRequest)
directions.calculate(completionHandler: { (response :MKDirectionsResponse?, error :NSError?) -> Void in
let route = response!.routes[0] as MKRoute
if route.steps.count > 0 {
for step in route.steps {
print(step.instructions)
}
}
self.mapView!.add((route.polyline), level: MKOverlayLevel.aboveRoads)
} as! MKDirectionsHandler)
} as! CLGeocodeCompletionHandler)
}
Go ahead and delete everything you wrote and do some research on how to write a new, better, cleaner function. Here is what worked for me and I hope it helps someone else.
func mapView(_ mapView: MKMapView, annotationView view:MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let placemark = MKPlacemark(coordinate: view.annotation!.coordinate, addressDictionary: nil)
let mapItem = MKMapItem(placemark: placemark)
let launchOptions = [MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeTransit]
self.title = title
mapItem.name = title
mapItem.openInMaps(launchOptions: launchOptions)
}

How to set visible region/zoom level for google maps IOS to show all markers added to the mapview

I have used Google maps in ios app, I wanna set zoom level dynamically depends on the search i made over map. Basically am adding pins by searching with city names or lat/long query. after every search am adding pins & i need to show all added markers by the recent search i made.
#IBOutlet weak var mapView: GMSMapView!
let camera = GMSCameraPosition.cameraWithLatitude(23.0793, longitude:
72.4957, zoom: 5)
mapView.camera = camera
mapView.delegate = self
mapView.myLocationEnabled = true
*** arry has dictionary object which has value of Latitude and Longitude. ***
let path = GMSMutablePath()
for i in 0..<arry.count {
let dict = arry[i] as! [String:AnyObject]
let latTemp = dict["latitude"] as! Double
let longTemp = dict["longitude"] as! Double
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: latTemp, longitude: longTemp)
marker.title = "Austrilia"
marker.appearAnimation = kGMSMarkerAnimationNone
marker.map = self.mapView
path.addCoordinate(CLLocationCoordinate2DMake(latTemp, longTemp))
}
let bounds = GMSCoordinateBounds(path: path)
self.mapView!.animateWithCameraUpdate(GMSCameraUpdate.fitBounds(bounds, withPadding: 50.0))
See this answer for a simple way to iterate over a given array of markers and then set the bounds accordingly.

Resources