воскресенье, 3 января 2016 г.

37. Карты. MKMapView Part 1

Большой перерыв получился с выполнения предыдущего урока в связи, с тем что я устроился разработчиком под IOS в крупную IT компанию.
Свободное время появилось только сейчас - в новогодние каникулы. Надеюсь пройти и закончить этот курс Алексея Скутаренко в ближайшее время. Так же планировал продолжить изучение разработки под IOS в новом курсе Алексея - Swift Development Course Beginner

Урок 37 и 38 посвящен библиотеке mapkit - картам Apple.
Видео 37 урока находится здесь.
Создается тестовый проект на базе Single View Application.
Для того, чтобы пользоваться картами - подключаем библиотеку в настройках проекта:
Linked FrameWorks and Libraries  добавляем (+) MapKit.framework.
В тех классах где будем применять карты необходимо так же  подключить главный загадочный файл MapKit.h (в нем подключаются все остальные заголовочные файлы фреймворка).

Создается Navigation controller и ViewController.
На ViewController помещаем MapView
Создаем аутлет MapView в заголочном файле.
Чтобы не было ошибки прописываем, что MKMapView будет объявлен (подключен) позже:

@class MKMapView;

Импорт лучше переносить внутрь реализации.
В .m файле подключаем MapKit.h:

#import <MapKit/MapKit.h>

Делаем наш ViewController делегатом MapView:

@interface ViewController () <MKMapViewDelegate>

Так же в сторибоард перетягиваем с MapView на ViewController связь и отмечаем Delegate.

Определяем функции делегата, для отображения когда какая запускается:

#pragma mark - MKMapViewDelegate


- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated{
    NSLog(@"regionWillChangeAnimated");
    
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
        NSLog(@"regionDidChangeAnimated");
}

- (void)mapViewWillStartLoadingMap:(MKMapView *)mapView{
        NSLog(@"mapViewWillStartLoadingMap");
}
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView{
        NSLog(@"mapViewDidFinishLoadingMap");
}
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error{
        NSLog(@"mapViewDidFailLoadingMap");
}

- (void)mapViewWillStartRenderingMap:(MKMapView *)mapView{
    NSLog(@"mapViewWillStartRenderingMap");
}
- (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered{
    NSLog(@"mapViewDidFinishRenderingMap");

}

 При приближении / удалении срабатывает функция regionWillChangeAnimated, которая сообщает что регион изменяется.
Регион в классе MKMapView - описывается переменной

@property (nonatomic) MKCoordinateRegion region

В свою очередь MKCoordinateRegion,  это структура

typedef struct {
CLLocationCoordinate2D center;
MKCoordinateSpan span;

} MKCoordinateRegion;

Которая определяется центром - center и радиусом/отклонением от центра span.

Координаты центра заданы типом - CLLocationCoordinate2D, который тоже является структурой состоящей из широты и долготы в градусах:

typedef struct {
CLLocationDegrees latitude;
CLLocationDegrees longitude;

} CLLocationCoordinate2D;

CLLocationDegrees - псевдоним  для типа double.

typedef double CLLocationDegrees;

span имеет тип  MKCoordinateSpan. Который определяется дельтой широты и долготы

typedef struct {
    CLLocationDegrees latitudeDelta;
    CLLocationDegrees longitudeDelta;

} MKCoordinateSpan;


Широта на экваторе - 0. На северном полюсе - 90 градусов. На южном полюсе - 90 градусов.
Долгота от 0 до 180 градусов если идти от нулевого меридиана против часовой стрелки. и 0 до - 180 если по часовой стрелке (смотря с северного полюса).
Широта горизонтальный срез. Долгота - вертикальный срез.

Создается кнопка Add  - добавить аннотацию на NavigationBar:

    UIBarButtonItem *addButton=[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(actionAddButton:)];
    self.navigationItem.rightBarButtonItem=addButton;

Для того, чтобы создавать аннотацию мы унаследуем новый класс TAMapAnnotation от NSObject и укажем, что он поддерживает протокол MKAnnotation:

@interface TAMapAnnotation : NSObject <MKAnnotation>

Добавляем обязательные и опциональные переменные содержащиеся в протоколе:

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomiccopy, nullable) NSString *title;
@property (nonatomiccopy, nullable) NSString *subtitle;

Убираем из них свойство  - readonly (это минимальное требование).

Теперь добавляем метод - обработчик нажатия кнопки Add на NavigationBar:

#pragma mark - action
-(void) actionAddButton :(UIBarButtonItem *) sender{

    TAMapAnnotation *annotation=[[TAMapAnnotation alloc]init];
    annotation.coordinate=self.mapView.region.center;
    annotation.title=@"Test Title";
    annotation.subtitle=@"SubTitle";
    [self.mapView addAnnotation:annotation];
}

Для того чтобы Пин с аннотацией падали с анимацией, определим метод:


- (nullable MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
    if ([annotation isKindOfClass:[MKUserLocation class]]){
        return nil;
    }
    static NSString *strPinIdentifier=@"Annotation";
    MKPinAnnotationView *pin =(MKPinAnnotationView*) [self.mapView dequeueReusableAnnotationViewWithIdentifier:strPinIdentifier];
    if (!pin) {
        pin=[[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:strPinIdentifier];
        pin.pinTintColor=[UIColor greenColor];
        pin.animatesDrop=YES;
        pin.canShowCallout=YES;
        pin.draggable=YES;
    }
    else{
        pin.annotation=annotation;
    }

    return pin;
}

По аналогии с ячейками таблицы создаем пин.

Для того, чтобы была возможность перетаскивать пин, необходимо  определить метод: 

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState
   fromOldState:(MKAnnotationViewDragState)oldState{
}


Добавим еще одну кнопку и метод обработчик для неё, который выводит все пины на экране (масштабирует и сдвигает карту для этого):

-(void) actionShowAll :(UIBarButtonItem *) sender{
//Создается пустой прямоугольник
    MKMapRect zoomRect= MKMapRectNull;

//в цикле проходим по всем аннотациям    
    for (id<MKAnnotation> annotation in self.mapView.annotations) {
//сохраняем координаты текущей аннотации
        CLLocationCoordinate2D location =annotation.coordinate;
//переводим координаты текущей аннотации в 2д формат координат
        MKMapPoint center=MKMapPointForCoordinate(location);
//определяем дельту для квадрата с аннотацией.
        static double delta = 20000;
//создаем вокруг аннотации квадрат +-дельта        
        MKMapRect rect=MKMapRectMake(center.x-delta, center.y-delta, delta*2, delta*2);
//объединяем в один прямоугольник
        zoomRect =MKMapRectUnion(zoomRect, rect);
    }

//исправляем полученный прямоугольник для вывода его на экран
    
    zoomRect=[self.mapView mapRectThatFits:zoomRect];
//выводим на экран получившийся прямоугольник с отступами 50 со всех сторон    
    [self.mapView setVisibleMapRect:zoomRect edgePadding:UIEdgeInsetsMake(50, 50, 50, 50) animated:YES];

}

Изменим метод для перемещения пина, таким образом, чтобы после завершения перемещения пина выводилась его координаты:

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState
   fromOldState:(MKAnnotationViewDragState)oldState{
//если состояние = конец перемещния
    if (newState==MKAnnotationViewDragStateEnding) {

//получаем координаты в градусах из аннотации
        CLLocationCoordinate2D location=view.annotation.coordinate;
//преобразуем в плоские координаты
        MKMapPoint point = MKMapPointForCoordinate(location);
//выводим координаты в градусах и в MKMapPoint
        NSLog(@"\nLocation={%@, %@}\nPoint = %@",@(location.latitude),@(location.longitude),MKStringFromMapPoint(point));
    }

}



Исходный текст этого урока здесь.
Продолжение в следующем уроке


Дополнение:

Для того, чтобы выводился запрос на показ локации необходимо
Объявить переменную:

@property (nonatomic,strong) CLLocationManager *locationManager;

Подписать ViewController на еще один протокол CLLocationManagerDelegate:

@interface ViewController () <MKMapViewDelegate,CLLocationManagerDelegate>

вставить следующий код в viewDidLoad:

    self.mapView.showsUserLocation=YES;
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    self.mapView.delegate = self;

    [self.locationManager requestWhenInUseAuthorization];
Так же в info.plist необходимо добавить новое поле типа String:
NSLocationWhenInUseUsageDescription с приветствием которое будет выводиться в запросе геопозиции, например: MegaMap would like to use your location.







Комментариев нет:

Отправить комментарий