IOS newb just learning Mapkit. I load a map in my app using MKPlacemark. However some users may want to use more advanced features such as driving directions and for this, I think, they would be better off launching the native app on top of mine (with my app still open in the background when they finish with the regular map app)
I know how to launch the native app from my app using MKMapItem. However, s there a way to launch the native app only after the user touches the place mark.
Here is code I am using.
-(void) geoCodeAndMapIt {
NSString* location = #"156 University Ave, Palo Alto, CA 94301";
NSLog(#"going to map this address: %#",location);
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc]
initWithPlacemark:topResult];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(placemark.coordinate, 5000, 5000);//5000 is meters
region.span.longitudeDelta /= 8.0;
region.span.latitudeDelta /= 8.0;
[self.mapView setRegion:region animated:YES];
[self.mapView addAnnotation:placemark];
// The following MKMapItem class launches the full blown native app. Commenting it out causes the map to load in the app. Otherwise, it fires up the native map app immediately in place of the previous app.
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:placemark];
mapItem.name = self.contact.first;
mapItem.phoneNumber = self.contact.tel;
NSDictionary *options = #{
MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsMapTypeKey:
[NSNumber numberWithInteger:MKMapTypeSatellite],
MKLaunchOptionsShowsTrafficKey:#YES
};
[mapItem setName:#"Name of your location"];
[mapItem openInMapsWithLaunchOptions:options];*/
}
}
];
[mapItem openInMapsWithLaunchOptions:options];
}
Thanks for any suggestions.
You should call the openInMaps only when the MKMapViewDelegate is called on didSelectAnnotation: for ex.
https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapViewDelegate_Protocol/index.html#//apple_ref/occ/intf/MKMapViewDelegate
To open the Maps app you could also build the URL yourself with the following:
UIApplication.sharedApplication().openURL(...)
Check this documentation here for the rest:
https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html
Related
In my project, I need to check user coordinates. If user lives in US, then I will enable a feature which shows air quality. Then further if user lives in Houston and surrounding area, then I will enable another feature which shows another feature, Ozone value. How could I achieve that? I could detect Houston and surrounding area with the following code, but I do not know how to find apart US states from Houston and World.
if((currentLocation.latitude>=28.930661 && currentLocation.latitude<=30.443953 && currentLocation.longitude>=-95.899668 && currentLocation.longitude<=-94.690486) ||
currentLocation.longitude == 0 || currentLocation.latitude == 0) {
// It is in the bounds
[self.opponentAvatarImageView removeFromSuperview];
UIActionSheet *actionsheet = [[UIActionSheet alloc] initWithTitle:#"Would you like to see ozone cloud in your area or a trace of your walking/driving/biking activity" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"OzoneMap", #"TraceMap", nil];
[actionsheet showInView:self.view];
}
Use the CLGeocoder class to pass in your location (CLLocation) and have it reverse geocode an address for you. You could even get specific with the City as well.
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:
^(NSArray* placemarks, NSError* error){
CLPlacemark *placemark = [placemarks firstObject];
if ([placemark.country isEqualToString:#"United States"]) {
// You're in the U.S.
}
}];
I am working on the mapkit in ios where I have to trace my route from my current location.
Now, I had assigned my current location i.e. latitude and longitude using the simulator(Debug->location->custom location) but when I try to trace my source I get my source location as nil and due to which I am not able to get the route.
[directionsRequest setSource:[MKMapItem mapItemForCurrentLocation]];
Can any one suggest the possible solution for my problem
Code
MKDirectionsRequest *directionsRequest = [[MKDirectionsRequest alloc] init];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:thePlacemark];
[directionsRequest setSource:[MKMapItem mapItemForCurrentLocation]];
[directionsRequest setDestination:[[MKMapItem alloc] initWithPlacemark:placemark]];
directionsRequest.transportType = MKDirectionsTransportTypeAutomobile;
MKDirections *directions = [[MKDirections alloc] initWithRequest:directionsRequest];
If you are using MapView then there is one delegate method in which you will get your current location without using CLLocationManager.
-(void)viewDidLoad
{
mapView.delegate=self;
}
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
`enter code here` //Here MKUserLocation will give you current Location and every time will method will call when the location is change...'
}
and if you want only user location then use
MKUserLocation *userlocation=mapView.userlocation
I think you are getting nil location because you are testing it in simulator please run it on real device
https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapItem_class/ states mapItemForCurrentLocation does not contain coordinate data for privacy reasons, but here is a simple workaround if you are using a map view and showsUserLocation true:
MKUserLocation* userLocation = self.mapView.userLocation;
MKMapItem* mapItemForCurrentLocation;
if(userLocation.isUpdating && userLocation.isBeingUpdated){
mapItemForCurrentLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:userLocation.location.coordinate addressDictionary:nil]];
mapItemForCurrentLocation.name = #"Current Location";
}
In my App it is possible to tap an button and then in the Google maps app it shows the route from your current location to the destination. It was working till some internal data structures changed and now it is not ;)
The error I get is:
Error Domain=kCLErrorDomain Code=8 "The operation couldn’t be completed. (kCLErrorDomain error 8.)"
Which I looked up in the documentation means (I think):
kCLErrorGeocodeFoundNoResult
The geocode request yielded no result.
Available in iOS 5.0 and later.
Declared in CLError.h.
Currently this is my code: (I'm using hardcoded location for testing right now):
+ (void) showRouteInMapsFromCurrentToLocation:(NSString *) location{
Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)]){
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:#"Kardingerweg 48, Groningen"
completionHandler:^(NSArray *placemarks, NSError *error) {
if(!error){
CLPlacemark *geocodedPlacemark = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc]
initWithCoordinate:geocodedPlacemark.location.coordinate
addressDictionary:geocodedPlacemark.addressDictionary];
if(placemark && geocodedPlacemark){
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:geocodedPlacemark.name];
NSDictionary *launchOptions = #{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving};
MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation];
[MKMapItem openMapsWithItems:#[currentLocationMapItem, mapItem] launchOptions:launchOptions];
}
}else{
NSLog(#"%#", [error description]);
}
}];
}
}
Error Code 8 is "kCLErrorGeocodeFoundNoResult", so I suspect you're searching for something with no results,
You are searching for 'Kardingerweg 48, Groningen' it is also not searching in apple map, try with different location.'Kardingerweg,Groningen'.
Thanks to the answer of Viruss mca, I now have the correct form which is:
Kardingerweg 48, NL-2165 VS Groningen
What I want to accomplish in my app is to get the current user location and display it onscreen in a UILabel. I would like to have an NSString of current user's location with a format similar to this: #"City, State/Country". It would be a one-time operation at the start of the app launch.
I have no prior experience with location in iOS and I would like to get some advice on this one - I'm sure it's quite a simple task.
The process is as follows:
Add CoreLocation.framework to your project. See Linking to a Library or a Framework. If you want to use the address book constants that I use below, you might want to add the AddressBook.framework to your project, too.
Start location services. For this purpose, the "significant change" service (less accurate, but lower power consumption) is probably sufficient for city-level accuracy.
When the location manager informs you of the user's location, then perform a reverse geocode of that location.
Stop location services.
Thus, that might look like:
#import <CoreLocation/CoreLocation.h>
#import <AddressBook/AddressBook.h>
#interface ViewController () <CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager *locationManager;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self startSignificantChangeUpdates];
}
- (void)startSignificantChangeUpdates
{
if ([CLLocationManager locationServicesEnabled])
{
if (!self.locationManager)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startMonitoringSignificantLocationChanges];
}
}
- (void)stopSignificantChangesUpdates
{
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = placemarks[0];
NSDictionary *addressDictionary = [placemark addressDictionary];
NSString *city = addressDictionary[(NSString *)kABPersonAddressCityKey];
NSString *state = addressDictionary[(NSString *)kABPersonAddressStateKey];
NSString *country = placemark.country;
self.label.text = [NSString stringWithFormat:#"%#, %#, %#", city, state, country];
}];
[self stopSignificantChangesUpdates];
}
Note, the location manager's notification of the location is contingent upon the user electing to share that with your app and will happen, even in the best case scenario, asynchronously. Likewise the reverse geocode happens asynchronously.
See Getting User Location from the Location Awareness Programming Guide.
Use -reverseGeocodeLocation:completionHandler: of CLGeocoder.
Try this code snippet, the only trick is that the CLPlacemark (see the Documentation for available info) you get back from the Geocoder has a bunch of info which isn't always consistent, this was one of my tries from an older project, trying to test for location, street name etc... test with your usage case to find a good match:
- (void)getLocationStringForCoordinates:(CLLocationCoordinate2D)coordinates {
if ( CLLocationCoordinate2DIsValid(coordinates) ) {
CLLocation *photoLocation = [[CLLocation alloc] initWithLatitude:coordinates.latitude longitude:coordinates.longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:photoLocation
completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *locationPlacemark = [placemarks lastObject];
// Location (popular name, street, area)
NSString *location = locationPlacemark.subLocality ? locationPlacemark.subLocality : (locationPlacemark.name ? locationPlacemark.name : locationPlacemark.thoroughfare);
// sometimes the location can be the same
// as the city name (for small villages), if so
// make sure location is nil to skip it
// else if
// the location name is not being used but is very short 9less then 20 letters, use that instead
if([locationPlacemark.name isEqualToString:locationPlacemark.locality] && [location isEqualToString:locationPlacemark.name])
location = #"";
else if ( ![locationPlacemark.name isEqualToString:location] && locationPlacemark.name.length < 20 )
location = locationPlacemark.name;
// city
NSString *city = locationPlacemark.subAdministrativeArea ? locationPlacemark.subAdministrativeArea : locationPlacemark.locality;
city = city.length > 0 ? [#", " stringByAppendingString:city] : city;
NSString *locationName = [NSString stringWithFormat:#"%#%#", location, city];
}];
}
}
I've found a really nice and simple to follow tutorial on this topic - http://www.appcoda.com/how-to-get-current-location-iphone-user/
Hope it will be helpful to others!
Take a look at the reverseGeocodeLocation:completionHandler: method for CLGeocoder:
http://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLGeocoder_class/Reference/Reference.html#//apple_ref/doc/uid/TP40009573
First you will have to use a CLLocationManager to get a CLLocation representing the user's current position.
I am trying to Reverse geocode location from Lat/Long value that I get earlier in the App and I would like from this coordinate to find the city name, country name and ISO.
I am currently using CLLocationManager to get actual location information with the folowing code:
//Auto geolocation and find city/country
locationManager.delegate=self;
//Get user location
[locationManager startUpdatingLocation];
[self.geoCoder reverseGeocodeLocation: locationManager.location completionHandler:
^(NSArray *placemarks, NSError *error) {
//Get nearby address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
locatedAtcountry = placemark.country;
locatedAtcity = placemark.locality;
locatedAtisocountry = placemark.ISOcountryCode;
//Print the location to console
NSLog(#"Estas en %#",locatedAtcountry);
NSLog(#"Estas en %#",locatedAtcity);
NSLog(#"Estas en %#",locatedAtisocountry);
[cityLabel setText:[NSString stringWithFormat:#"%#,",locatedAtcity]];
[locationLabel setText:[NSString stringWithFormat:#"%#",locatedAtcountry]];
//Set the label text to current location
//[locationLabel setText:locatedAt];
}];
It is working perfectly but, It is possible to do the same from Long/Lat value that I had already saved in the device and not with the current location like on the actual code ?
Update and solution:
Thanks Mark for the answer, I finally use the following code to get info from saved coordinate:
CLLocation *location = [[CLLocation alloc] initWithLatitude:37.78583400 longitude:-122.40641700];
[self.geoCoder reverseGeocodeLocation: location completionHandler:
^(NSArray *placemarks, NSError *error) {
//Get nearby address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
locatedAtcountry = placemark.country;
locatedAtcity = placemark.locality;
locatedAtisocountry = placemark.ISOcountryCode;
//Print the location to console
NSLog(#"Estas en %#",locatedAtcountry);
NSLog(#"Estas en %#",locatedAtcity);
NSLog(#"Estas en %#",locatedAtisocountry);
[cityLabel setText:[NSString stringWithFormat:#"%#",locatedAtcity]];
[locationLabel setText:[NSString stringWithFormat:#"%#",locatedAtcountry]];
//Set the label text to current location
//[locationLabel setText:locatedAt];
}];
Yes. Create a CLLocation object using the initWithLatitude:longitude: method using your saved lat/lon values, and pass that to reverseGeocodeLocation:.
I am surprised that you say this is working (although, if you're on the simulator, location services are simulated anyway, which might be the reason) because when you call startUpdatingLocation, your implementation of CLLocationManagerDelegate methods like locationManager:didUpdateToLocation:fromLocation: get called. (You've implemented these right?) It is only when this (and other) delegate method is called that you can be certain that you have successfully determined the user's location.
You may want to read up on the CLLocationManagerDelegate protocol and on Location Services best practices as documented by Apple.