Pull to refresh

Маршрут в iOS приложениях

Reading time6 min
Views5.4K
Написав несколько приложений для iPhone, в которых использование карты является одним из главных вариантов использования, я задумался над тем, что куча объектов на карте это очень круто, информативно и наглядно, но нехватает маршрутов от одной точки до другой.
Ниже я поделюсь своим опытом реализации данного 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
______________________
Текст подготовлен в Хабра Редакторе от © SoftCoder.ru
Tags:
Hubs:
Total votes 7: ↑6 and ↓1+5
Comments8

Articles