Вандад Нахавандипур - iOS. Приемы программирования
Решение
Загружаем произвольное изображение в экземпляр класса UIImage и присваиваем этот экземпляр свойству image экземпляра 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.canShowCallout = YES;
}
UIImage *pinImage = [UIImage imageNamed:@"BluePin.png"];
if (pinImage!= nil){
annotationView.image = pinImage;
}
result = annotationView;
return result;
}
В данном коде отображаем картинку под названием BluePin.png (в пакете нашего приложения) для любого маркера, который ставится на карте. Определение реализации класса MyAnnotation приводится в разделе 9.5.
Обсуждение
Объект делегата, относящийся к классу MKMapView, должен соответствовать протоколу MKMapViewDelegate и реализовывать метод mapView: viewForAnnotation:. Возвращаемое значение этого метода является экземпляром класса MKAnnotationView. Любой объект, являющийся подклассом вышеупомянутого класса, по умолчанию наследует свойство image. Если присвоить этому свойству такое значение, то мы заменим стандартное значение, предоставляемое во фреймворке Map Kit. Результат показан на рис. 9.4.
Рис. 9.4. Наше собственное изображение, показанное в картографическом виде
См. также
Раздел 9.5.
9.7. Преобразование обычных адресов в данные широты и долготы
Постановка задачи
Имеется адрес определенного места, необходимо найти его географические координаты (широту и долготу).
Решение
Воспользуйтесь методом geocodeAddressString: completionHandler: из класса CLGeocoder.
Обсуждение
Обратное геокодирование (Reverse Geocoding) — это процесс получения обычного адреса (то есть страны, города и т. д.) на базе известного пространственного расположения (координат широты и долготы). В свою очередь, геокодирование — это процесс нахождения пространственного расположения в сетке координат на основе известного адреса. Функции геокодирования и обратного геокодирования заключены в классе CLGeocoder фреймворка Core Location.
Пространственное местоположение геокодируется путем передачи адреса в формате NSString методу geocodeAddressString: completionHandler:, относящемуся к классу CLGeocoder. Параметр completionHandler этого метода принимает блоковый объект, не возвращающий никакого значения и имеющий два параметра:
• массив меток (типа NSArray). Метками будут обозначены те точки на карте, которые соответствуют критериям поискового запроса;
• ошибку (типа NSError), которая будет преобразована в код ошибки, если геокодирование не удастся.
Итак, сначала объявим свойство типа CLGeocoder:
#import «ViewController.h»
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()
@property (nonatomic, strong) CLGeocoder *myGeocoder;
@end
@implementation ViewController
Идем дальше. Реализуем код для геокодирования адреса:
— (void)viewDidLoad{
[super viewDidLoad];
/* У нас есть адрес. */
NSString *oreillyAddress =
@"1005 Gravenstein Highway North, Sebastopol, CA 95472, USA";
self.myGeocoder = [[CLGeocoder alloc] init];
[self.myGeocoder
geocodeAddressString: oreillyAddress
completionHandler: ^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0 &&
error == nil){
NSLog(@"Found %lu placemark(s).", (unsigned long)[placemarks count]);
CLPlacemark *firstPlacemark = [placemarks objectAtIndex:0];
NSLog(@"Longitude = %f",
firstPlacemark.location.coordinate.longitude);
NSLog(@"Latitude = %f", firstPlacemark.location.coordinate.latitude);
}
else if ([placemarks count] == 0 &&
error == nil){
NSLog(@"Found no placemarks.");
}
else if (error!= nil){
NSLog(@"An error occurred = %@", error);
}
}];
}
Как только программа будет запущена (даже в симуляторе), в окне консоли появятся следующие значения (при наличии активного сетевого соединения):
Found 1 placemark(s).
Longitude = -122.8 41135
Latitude = 38.4 10373
См. также
Раздел 9.8.
9.8. Преобразование данных широты и долготы в обычные адреса
Постановка задачи
Имеются значения широты и долготы определенной точки в пространстве. Необходимо получить ее адрес.
Решение
Получение обычного адреса на основании известных пространственных координат (x и y) называется обратным геокодированием. Для выполнения такой операции нужно создать и использовать экземпляр класса CLGeocoder, а также предоставить блоковый объект завершения. При этом необходимо гарантировать, что блоковый объект не имеет возвращаемого значения и принимает два параметра:
• массив меток (типа NSArray). Метками будут обозначены те точки на карте, которые соответствуют критериям поискового запроса;
• ошибку (типа NSError), которая будет преобразована в код ошибки, если обратное геокодирование не удастся.
Инстанцировав объект типа CLGeocoder, мы используем его метод reverseGeocodeLocation: completionHandler: для выполнения обратного геокодирования.
Верхняя часть. m-файла простого контроллера вида, применяемого для этой цели, определяется следующим образом:
#import «ViewController.h»
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()
@property (nonatomic, strong) CLGeocoder *myGeocoder;
@end
@implementation ViewController
В ходе загрузки вида можно выполнить обратное геокодирование:
— (void)viewDidLoad{
[super viewDidLoad];
CLLocation *location = [[CLLocation alloc]
initWithLatitude:+38.41 12810
longitude:-122.8409780f];
self.myGeocoder = [[CLGeocoder alloc] init];
[self.myGeocoder
reverseGeocodeLocation: location
completionHandler: ^(NSArray *placemarks, NSError *error) {
if (error == nil && placemarks.count > 0){
CLPlacemark *placemark = placemarks[0];
/* Результаты получены. */
NSLog(@"Country = %@", placemark.country);
NSLog(@"Postal Code = %@", placemark.postalCode);
NSLog(@"Locality = %@", placemark.locality);
}
else if (error == nil &&
[placemarks count] == 0){
NSLog(@"No results were returned.");
}
else if (error!= nil){
NSLog(@"An error occurred = %@", error);
}
}];
}
— (void)viewDidUnload{
[super viewDidUnload];
self.myGeocoder = nil;
}
Если операция завершится успешно, то в массиве placemarks будут содержаться объекты типа CLPlacemark. Эти объекты будут отмечать адреса, удовлетворяющие значениям широты и долготы, которые мы сообщили методу reverseGeocodeLocation: completionHandler:. Итак, все, что от нас требуется, — убедиться в отсутствии ошибок и в том, что в массиве меток есть как минимум одна метка.