📜 ⬆️ ⬇️

Route in iOS applications

Having written several applications for the iPhone, in which using the map is one of the main use cases, I thought about the fact that a bunch of objects on the map is very cool, informative and visual, but lacks routes from one point to another.
Below I will share my experience in implementing this use-case.

Two visions

My knowledge of the iOS SDK led me to 2 possibilities of implementation:

The first method is complicated by the fact that it is necessary to have a source of control points for the route, and even then it is not known how many of these points it is necessary for the lines to lie on all the bends of the roads, etc. Therefore, I think the second method will satisfy the majority with its capabilities.

Implementation

Consider the implementation of a specific example. For work we need MapKit.framework.
We assume that we have an instance of UIViewController, which is on the UINavigationController stack and displays MKMapView. On the map we will display UserLocation and the point where we need to go. In my practice, it often happens that our destination is already described by the address and we can only get the address of the current position of the user.

Let's try in order.
Through the constructor we transmit data that describes the destination: coordinates, name and address of the place. In the designer, we create an annotation for this place and a “Route” button, which will be displayed in the UINavigationBar and become available to the user after the program finds out the address of its current location.
')
@interface RouteController : UIViewController <MKMapViewDelegate, MKReverseGeocoderDelegate> {
MKPointAnnotation * _pointAnnotaion;
NSString * _selfAddress;
}
- ( id ) initWithPlaceCoordinate : ( CLLocationCoordinate2D ) coorinate
placeName : ( NSString * ) name
placeAddress : ( NSString * ) address;
@end

@implementation RouteController
- ( id ) initWithPlaceCoordinate : ( CLLocationCoordinate2D ) coorinate placeName : ( NSString * ) name placeAddress : ( NSString * ) address {
self = [ super init ] ;
if ( self ) {
self.navigationItem.rightBarButtonItem = [ [ [ UIBarButtonItem alloc ] initWithTitle : @ ""
style : UIBarButtonItemStyleBordered
target : self
action : @selector ( routeAction ) ]
autorelease ] ;
self.navigationItem.rightBarButtonItem.enabled = NO ;

_pointAnnotaion = [ MKPointAnnotation new ] ;
_pointAnnotaion.title = name;
_pointAnnotaion.subtitle = address;
_pointAnnotaion.coordinate = coorinate;
}
return self;
}

- ( void ) dealloc {
[ _pointAnnotaion release ] ;
[ _selfAddress release ] ;

[ super dealloc ] ;
}


Suppose that the controller is initiated without the xib file and self.view will point to the MKMapView instance.

- ( void ) loadView {
MKMapView * mapView = [ [ [ MKMapView alloc ] initWithFrame : [ UIScreen mainScreen ] .bounds ] autorelease ] ;
mapView.showsUserLocation = YES ;
mapView.delegate = self;
[ mapView addAnnotation : _pointAnnotaion ] ;

self.view = mapView;
}


As soon as the map has determined the coordinates of the user, we will use MKReverseGeocoder to get the address by these coordinates (MKReverseGeocoder requires use according to certain rules, but I will not elaborate on this here).

#pragma mark - Map view delegate

- ( void ) mapView : ( MKMapView * ) mapView didUpdateUserLocation : ( MKUserLocation * ) userLocation {
MKReverseGeocoder * geocoder = [ [ MKReverseGeocoder alloc ] initWithCoordinate : userLocation.coordinate ] ;
geocoder.delegate = self;
[ geocoder start ] ;
}

#pragma mark - Reverse geocoder delegate

- ( void ) reverseGeocoder : ( MKReverseGeocoder * ) geocoder didFindPlacemark : ( MKPlacemark * ) placemark {
[ geocoder autorelease ] ;
NSArray * formattedAddress = [ placemark.addressDictionary objectForKey : @ "FormattedAddressLines" ] ;
[ _selfAddress release ] ;
_selfAddress = [ [ formattedAddress componentsJoinedByString : @ "," ] retain ] ;

MKMapView * mapView = ( MKMapView * ) self.view;
for ( id<MKAnnotation> annotation in [ mapView annotations ] ) {
if ( [ annotation isKindOfClass : [ MKUserLocation class ] ] ) {
( ( MKUserLocation * ) annotation ) .subtitle = _selfAddress;
}
}
self.navigationItem.rightBarButtonItem.enabled = YES ;
}

- ( void ) reverseGeocoder : ( MKReverseGeocoder * ) geocoder didFailWithError : ( NSError * ) error {
[ geocoder autorelease ] ;
}


After we have received the address, we save it in the _selfAddress variable and make the “Route” button accessible to the user. After clicking the "Marshurt" button, it remains to make a request at maps.google.com/maps ? daddr = & saddr =, where daddr and saddr are the end address and the start address of the route, respectively.

#pragma mark - Private methods

- ( NSString * ) escapedStringFromString : ( NSString * ) string {
NSString * result = ( NSString * ) CFURLCreateStringByAddingPercentEscapes (
NULL , /* allocator */
( CFStringRef ) string ,
NULL , /* charactersToLeaveUnescaped */
( CFStringRef ) @ "!*'();:@&=+$,/?%#[]" ,
kCFStringEncodingUTF8 ) ;
return [ result autorelease ] ;
}

#pragma mark - Public methods

- ( void ) routeAction {
NSString * daddr = [ self escapedStringFromString : [ _pointAnnotaion subtitle ] ] ;
NSString * saddr = [ self escapedStringFromString : _selfAddress ] ;
NSString * routeURL = [ NSString stringWithFormat : @ "maps.google.com/maps?daddr=%@&saddr=%@" , daddr, saddr ] ;
[ [ UIApplication sharedApplication ] openURL : [ NSURL URLWithString : routeURL ] ] ;
}
@end


If the URL was compiled correctly, the Maps application will open, in which the route will be built.

Materials on the topic

Apple Url Scheme
______________________
The text was prepared in the Habr Editor from © SoftCoder.ru

Source: https://habr.com/ru/post/126586/


All Articles