Написав несколько приложений для iPhone, в которых использование карты является одним из главных вариантов использования, я задумался над тем, что куча объектов на карте это очень круто, информативно и наглядно, но нехватает маршрутов от одной точки до другой.
Ниже я поделюсь своим опытом реализации данного use-case’а.
Мои познания iOS SDK навели меня на 2 возможности реализации:
Первый способ сложен тем, что необходимо иметь источник контрольных точек для маршрута, да и потом еще неизвестно сколько этих точек надо, чтобы линии ложились на все изгибы дорог и т.д. Поэтому я думаю второй способ удовлетворит своими возможностями большинство.
Рассмотрим реализацию на конкретном примере. Для работы нам понадобится MapKit.framework.
Будем считать, что у нас есть экземпляр UIViewController’а, который находится в стеке у UINavigationController’а и отображает MKMapView. На карте у нас будет отображаться UserLocation и точка, куда нам нужно попасть. В моей практике чаще бывает так, что пункт назначения у нас уже описан адресом и нам остается только получить адрес текущего положения пользователя.
Попробуем по порядку.
Через конструктор передаем данные, которые описывают место назначения: координаты, название и адрес места. В конструкторе создаем аннотацию для этого места и кнопку «Маршрут», которая будет отображаться в UINavigationBar и станет доступна пользователя после того как программа узнает адрес его текущего размещения.
Предположим, что контроллер у нас инициируется без xib файла и self.view будет указывать на экземпляр MKMapView.
Как только карта определила координаты пользователя воспользуемся MKReverseGeocoder, чтобы получить адрес по этим координатам (MKReverseGeocoder требует использование по определенным правилам, но я не буду здесь подробно останавливаться на этом).
После того как адрес мы получили, сохраняем его в переменной _selfAddress и делаем кнопку «Маршрут» доступной для пользователя. После того как нажмут кнопку «Маршурт» на остается сделать запрос по адресу maps.google.com/maps? daddr=&saddr=, где daddr и saddr адрес конца и адрес начала маршрута соответственно.
Если URL был составлен правильно, то откроется приложение «Карты», в котором и построится маршрут.
Apple Url Scheme
______________________
Ниже я поделюсь своим опытом реализации данного use-case’а.
Два видения
Мои познания iOS SDK навели меня на 2 возможности реализации:
- Сложный. С помощью MKOverlayView.
- Попроще. С помощью приложения «Карты», установленного по-умолчанию.
Первый способ сложен тем, что необходимо иметь источник контрольных точек для маршрута, да и потом еще неизвестно сколько этих точек надо, чтобы линии ложились на все изгибы дорог и т.д. Поэтому я думаю второй способ удовлетворит своими возможностями большинство.
Реализация
Рассмотрим реализацию на конкретном примере. Для работы нам понадобится MapKit.framework.
Будем считать, что у нас есть экземпляр UIViewController’а, который находится в стеке у UINavigationController’а и отображает MKMapView. На карте у нас будет отображаться UserLocation и точка, куда нам нужно попасть. В моей практике чаще бывает так, что пункт назначения у нас уже описан адресом и нам остается только получить адрес текущего положения пользователя.
Попробуем по порядку.
Через конструктор передаем данные, которые описывают место назначения: координаты, название и адрес места. В конструкторе создаем аннотацию для этого места и кнопку «Маршрут», которая будет отображаться в UINavigationBar и станет доступна пользователя после того как программа узнает адрес его текущего размещения.
@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];
}
Предположим, что контроллер у нас инициируется без xib файла и self.view будет указывать на экземпляр MKMapView.
- (void)loadView {
MKMapView * mapView = [[[MKMapView alloc] initWithFrame:[UIScreen mainScreen].bounds] autorelease];
mapView.showsUserLocation = YES;
mapView.delegate = self;
[mapView addAnnotation:_pointAnnotaion];
self.view = mapView;
}
Как только карта определила координаты пользователя воспользуемся MKReverseGeocoder, чтобы получить адрес по этим координатам (MKReverseGeocoder требует использование по определенным правилам, но я не буду здесь подробно останавливаться на этом).
#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];
}
После того как адрес мы получили, сохраняем его в переменной _selfAddress и делаем кнопку «Маршрут» доступной для пользователя. После того как нажмут кнопку «Маршурт» на остается сделать запрос по адресу maps.google.com/maps? daddr=&saddr=, где daddr и saddr адрес конца и адрес начала маршрута соответственно.
#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
Если URL был составлен правильно, то откроется приложение «Карты», в котором и построится маршрут.
Материалы по теме
Apple Url Scheme
______________________