iOS. Приемы программирования Нахавандипур Вандад
Необходимо указать пользователю конкретное место на карте.
Решение
Воспользуйтесь встроенными аннотациями для картографических видов. Для этого выполните следующие шаги.
1. Создайте новый класс и назовите его MyAnnotation.
2. Убедитесь, что этот класс соответствует протоколу MKAnnotation.
3. Определите свойство типа CLLocationCoordinate2D для этого класса и назовите данное свойство coordinate. Убедитесь, что задали это свойство как readonly (только для чтения), поскольку свойство coordinate в соответствии с протоколом MKAnnotation определяется как readonly.
4. Далее можно (но не обязательно) определить два свойства типа NSString, а именно h2 и subh2, которые могут содержать заголовок и подзаголовок вашего аннотирующего вида. Оба этих свойства также будут readonly.
5. Создайте для вашего класса метод-инициализатор. Этот метод будет принимать параметр типа CLLocationCoordinate2D. В этом методе присвойте переданный параметр местоположения тому свойству, которое мы определили на этапе 3. Поскольку это свойство является readonly, его невозможно присвоить с помощью кода вне области видимости данного класса. Следовательно, инициализатор этого класса действует здесь как перемычка и позволяет опосредованно присваивать значение этому свойству. Такие же операции мы осуществим со свойствами h2 и subh2.
6. Инстанцируйте класс MyAnnotation и добавьте его к вашей карте с помощью метода addAnnotation:, относящегося к классу MKMapView.
Обсуждение
Как было рассказано в подразделе «Решение» данного раздела, нам следует создать объект, соответствующий протоколу MKAnnotation, а позже инстанцировать этот объект и передать ему карту для отображения. h-файл этого объекта будет записываться так:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface MyAnnotation: NSObject <MKAnnotation>
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy, readonly) NSString *h2;
@property (nonatomic, copy, readonly) NSString *subh2;
— (instancetype)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
h2:(NSString *)paramTitle
subTitle:(NSString *)paramSubTitle;
@end
В.m-файле класса MyAnnotation мы создаем класс, отвечающий за отображение геолокационной информации, и делаем это следующим образом:
#import «MyAnnotation.h»
@implementation MyAnnotation
— (instancetype)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
h2:(NSString *)paramTitle
subTitle:(NSString *)paramSubTitle{
self = [super init];
if (self!= nil){
coordinate = paramCoordinates;
h2 = paramTitle;
subh2 = paramSubTitle;
}
return(self);
}
@end
Позже мы инстанцируем этот класс и добавим его к нашей карте, например к. m-файлу того контроллера вида, который создает и отображает картографический вид:
#import «ViewController.h»
#import «MyAnnotation.h»
#import <MapKit/MapKit.h>
@interface ViewController () <MKMapViewDelegate>
@property (nonatomic, strong) MKMapView *myMapView;
@end
@implementation ViewController
— (void)viewDidLoad {
[super viewDidLoad];
/* Создаем карту такого же размера, как и наш вид. */
self.myMapView = [[MKMapView alloc]
initWithFrame: self.view.bounds];
self.myMapView.delegate = self;
/* Задаем для карты тип Standard. */
self.myMapView.mapType = MKMapTypeStandard;
self.myMapView.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
/* Добавляем ее к нашему виду. */
[self.view addSubview: self.myMapView];
/* Это просто один образец местоположения. */
CLLocationCoordinate2D location =
CLLocationCoordinate2DMake(50.8219 16929 07181, -0.13 81176 71012 87842);
/* Создаем аннотацию, используя информацию о местоположении. */
MyAnnotation *annotation =
[[MyAnnotation alloc] initWithCoordinates: location
h2:@"My Title"
subTitle:@"My Sub Title"];
/* И наконец, добавляем аннотацию на карту. */
[self.myMapView addAnnotation: annotation];
@end
На рис. 9.2 показан вывод данной программы в симуляторе iPhone.
Рис. 9.2. Интегрированный в систему стандартный маркер, отображенный на карте
9.5. Отображение разноцветных маркеров в картографическом виде
Постановка задачи
По умолчанию маркеры-индикаторы, которыми отмечаются точки на карте, — красного цвета. Необходимо отображать маркеры различных цветов, а не только стандартного красного.
Решение
Возвращайте вашему картографическому виду экземпляры MKPinAnnotationView. Это делается с помощью метода делегата mapView: viewForAnnotation:.
Каждая аннотация, добавляемая к экземпляру MKMapView, соответствует конкретному виду, который отображается поверх картографического вида. Такие всплывающие виды называются аннотирующими (Annotation Views).
Аннотирующий вид — это объект типа MKAnnotationView, он является подклассом от UIView. Если объект делегата картографического вида реализует метод делегата mapView: viewForAnnotation:, то объект делегата должен будет возвращать экземпляры класса MKAnnotationView, чтобы отображать (а при необходимости — настраивать) аннотирующие виды, которые выводятся поверх картографического вида.
Обсуждение
Чтобы обеспечить в нашей программе возможность настройки цвета меток (цвет будем выбирать из стандартной палитры, предусмотренной для меток в SDK), которые ставятся на картографическом виде для представления аннотаций, нам понадобится возвращать в методе делегата mapView: viewForAnnotation: не экземпляр класса MKAnnotationView, а экземпляр класса MKPinAnnotationView. Не забывайте, что класс MKPinAnnotationView является подклассом MKAnnotationView.
— (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id <MKAnnotation>)annotation{
MKAnnotationView *result = nil;
if ([annotation isKindOfClass: [MyAnnotation class]] == NO){
return result;
}
if ([mapView isEqual: self.myMapView] == NO){
/* Мы собираемся обработать это событие только для того Map View,
который создали ранее. */
return result;
}
/* Сначала приводим тип той аннотации, для которой этот Map View
запустил данное сообщение делегата. */
MyAnnotation *senderAnnotation = (MyAnnotation *)annotation;
/* С помощью метода класса, определенного нами в собственном
классе аннотаций, мы попытаемся сделать многоразовый идентификатор
для того маркера, который сейчас создаем. */
NSString *pinReusableIdentifier =
[MyAnnotation
reusableIdentifierforPinColor: senderAnnotation.pinColor];
/* Пользуясь идентификатором, полученным ранее, попытаемся
повторно применить маркер в отправляющем Map View. */
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)
[mapView
dequeueReusableAnnotationViewWithIdentifier: pinReusableIdentifier];
if (annotationView == nil){
/* Если нам не удастся повторно использовать имеющийся маркер,
создадим новый. */
annotationView = [[MKPinAnnotationView alloc]
initWithAnnotation: senderAnnotation
reuseIdentifier: pinReusableIdentifier];
/* Убеждаемся, что видны выноски поверх каждого маркера в случае,
если мы присвоили каждому маркеру заголовок и/или подзаголовок. */
[annotationView setCanShowCallout: YES];
}
/* Теперь (независимо от того, использовали мы многоразовый маркер
или создали новый) убеждаемся, что цвет маркера совпадает с цветом
аннотации. */
annotationView.pinColor = senderAnnotation.pinColor;
result = annotationView;
return result;
}
При многократном использовании аннотирующего вида ему присваивается идентификатор (строка NSString). Определяя, маркер какого типа вы хотели бы отобразить на карте, и задавая уникальный идентификатор для маркера каждого типа (например, к одному типу могут относиться красные маркеры, а к другому — синие), следует многократно использовать маркеры нужного типа, применяя метод экземпляра dequeueReusableAnnotationViewWithIdentifier:, относящийся к классу MKMapView. Это показано в следующем коде.
Мы запрограммировали механизм получения уникальных идентификаторов каждого маркера в собственном классе MyAnnotation. Вот. h-файл класса MyAnnotation:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
/* Это стандартные цвета меток, присутствующие в SDK. Мы задаем уникальные
идентификаторы для каждого маркера в соответствии с его цветом, чтобы
позже можно было снова использовать созданные ранее маркеры в связи
с тем же цветом, для которого они создавались. */
extern NSString *const kReusablePinRed;
extern NSString *const kReusablePinGreen;
extern NSString *const kReusablePinPurple;
@interface MyAnnotation: NSObject <MKAnnotation>
/* unsafe_unretained, так как это не объект. Этот шаг можно пропустить
и оставить принятие этого решения компилятору. weak или strong
не сработают, так как это не объект. */
@property (nonatomic, unsafe_unretained, readonly)
CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *h2;
@property (nonatomic, copy) NSString *subh2;
/* unsafe_unretained по той же причине, что и для свойства coordinate */
@property (nonatomic, unsafe_unretained) MKPinAnnotationColor pinColor;
— (instancetype)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
h2:(NSString*)paramTitle
subTitle:(NSString*)paramSubTitle;
+ (NSString *) reusableIdentifierforPinColor
:(MKPinAnnotationColor)paramColor;
@end
Аннотация не то же самое, что аннотирующий вид. Аннотация — это место, которое вы хотите указать на карте, а аннотирующий вид — это визуальное представление, в котором эта аннотация всплывает над картой (то есть вид). Класс MyAnnotation соответствует аннотации, а не аннотирующему виду. Когда мы создаем аннотацию путем инстанцирования класса MyAnnotation, мы можем присвоить ей цвет, задействовав определенное и реализованное нами же свойство pinColor. Когда картографический вид должен будет отобразить аннотацию, картографический вид вызовет метод делегата mapView: viewForAnnotation: и запросит у этого делегата аннотирующий вид. В параметре forAnnotation данного метода сообщается аннотация, которую необходимо отобразить. Получая ссылку на аннотацию, мы можем привести тип аннотации к экземпляру MyAnnotation, получить ее свойство pinColor и, основываясь на этих данных, создать экземпляр класса MKPinAnnotationView. У этого экземпляра будет информация о заданном цвете маркера, которую мы вернем картографическому виду.
Вот. m-файл MyAnnotation:
#import «MyAnnotation.h»
NSString *const kReusablePinRed = @"Red";
NSString *const kReusablePinGreen = @"Green";
NSString *const kReusablePinPurple = @"Purple";
@implementation MyAnnotation
+ (NSString *) reusableIdentifierforPinColor
:(MKPinAnnotationColor)paramColor{
NSString *result = nil;
switch (paramColor){
case MKPinAnnotationColorRed:{
result = REUSABLE_PIN_RED;
break;
}
case MKPinAnnotationColorGreen:{
result = REUSABLE_PIN_GREEN;
break;
}
case MKPinAnnotationColorPurple:{
result = REUSABLE_PIN_PURPLE;
break;
}
}
return result;
}
— (instancetype)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
h2:(NSString*)paramTitle
subTitle:(NSString*)paramSubTitle{
self = [super init];
if (self!= nil){
_coordinate = paramCoordinates;
_h2 = paramTitle;
_subh2 = paramSubTitle;
_pinColor = MKPinAnnotationColorGreen;
}
return self;
}
@end
Выполнив реализацию класса MyAnnotation, его нужно задействовать в приложении (в данном примере мы воспользуемся контроллером вида). Вот верхняя часть файла реализации контроллера вида:
#import «ViewController.h»
#import «MyAnnotation.h»
#import <MapKit/MapKit.h>
@interface ViewController () <MKMapViewDelegate>
@property (nonatomic, strong) MKMapView *myMapView;
@end
@implementation ViewControllerРеализация в файле. m будет такой:
— (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id <MKAnnotation>)annotation{
MKAnnotationView *result = nil;
if ([annotation isKindOfClass: [MyAnnotation class]] == NO){
return result;