zoukankan      html  css  js  c++  java
  • iOS学习笔记20-地图(二)MapKit框架

    一、地图开发介绍

    从iOS6.0开始地图数据不再由谷歌驱动,而是改用自家地图,当然在国内它的数据是由高德地图提供的。

    在iOS中进行地图开发主要有三种方式:
    • 利用MapKit框架进行地图开发,利用这种方式可以对地图进行精准的控制
    • 调用苹果官方自带的地图应用,主要用于一些简单的地图应用,无法精确控制
    • 使用第三方地图开发SDK库

    用得最多的还是MapKit,所以这节就只讲MapKit的使用。

    二、MapKit核心类

    MapKit的核心类为地图展示控件MKMapView,以下是常用的属性、对象方法以及代理方法。

    1. 属性:

    /* 用户位置跟踪 */
    @property (nonatomic) BOOL showsUserLocation;/*< 是否在地图上标注用户位置 */
    @property (nonatomic, readonly) MKUserLocation *userLocation;/*< 用户位置 */
    @property (nonatomic) MKUserTrackingMode userTrackingMode;/*< 用户跟踪类型 */
    typedef NS_ENUM(NSInteger, MKUserTrackingMode) {
    	MKUserTrackingModeNone = 0, /*< 不跟踪 */
    	MKUserTrackingModeFollow, /*< 跟踪 */
    	MKUserTrackingModeFollowWithHeading,  /*< 导航跟踪 */
    };
    /* 设置地图配置项 */
    @property (nonatomic) MKMapType mapType;/*< 地图类型 */
    @property (nonatomic, readonly) NSArray *annotations;/*< 大头针数组 */
    typedef NS_ENUM(NSUInteger, MKMapType) {
        MKMapTypeStandard = 0,/*< 标准地图 */
        MKMapTypeSatellite,/*< 卫星地图 */
        MKMapTypeHybrid,/*< 混合模式(标准+卫星) */
        MKMapTypeSatelliteFlyover,/*< 3D立体卫星(iOS9.0) */
        MKMapTypeHybridFlyover,/*< 3D立体混合(iOS9.0) */
    }
    /* 设置地图控制项 */
    @property (nonatomic) BOOL zoomEnabled;/*< 是否可以缩放 */
    @property (nonatomic) BOOL scrollEnabled;/*< 是否可以滚动 */
    @property (nonatomic) BOOL rotateEnabled;/*< 是否可以旋转 */
    @property (nonatomic) BOOL pitchEnabled;/*< 是否显示3D视角 */
    /* 设置地图显示项 */
    @property (nonatomic) BOOL showsBuildings;/*< 是否显示建筑物,只影响标准地图 */
    @property (nonatomic) BOOL showsTraffic;/*< 是否显示交通,iOS9 */
    @property (nonatomic) BOOL showsCompass;/*< 是否显示指南针,iOS9 */
    @property (nonatomic) BOOL showsScale;/*< 是否显示比例尺,iOS9 */
    

    所谓大头针就是地图上显示的这个标注:
    大头针图片

    2. 对象方法:

    /* 添加大头针 */
    - (void)addAnnotation:(id <MKAnnotation>)annotation;
    - (void)addAnnotations:(NSArray<id<MKAnnotation>> *)annotations;
    /* 删除大头针 */
    - (void)removeAnnotation:(id <MKAnnotation>)annotation;
    - (void)removeAnnotations:(NSArray<id<MKAnnotation>> *)annotations;
    /* 选中大头针与取消选中大头针 */
    - (void)selectAnnotation:(id <MKAnnotation>)annotation 
                    animated:(BOOL)animated;
    - (void)deselectAnnotation:(id <MKAnnotation>)annotation 
                      animated:(BOOL)animated;
    /* 获取大头针视图 */
    - (MKAnnotationView *)viewForAnnotation:(id <MKAnnotation>)annotation;
    /* 从缓冲池中取出大头针视图控件 */
    - (MKAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier;
    /* 设置显示区域以及地图中心坐标 */
    - (void)setRegion:(MKCoordinateRegion)region 
             animated:(BOOL)animated;
    - (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate 
                       animated:(BOOL)animated;
    /* 经纬度坐标转UIKit坐标,UIKit坐标转经纬度坐标 */
    - (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate 
                   toPointToView:(UIView *)view;
    - (CLLocationCoordinate2D)convertPoint:(CGPoint)point 
                      toCoordinateFromView:(UIView *)view;
    

    3. 常用代理方法MKMapViewDelegate

    /* 地图加载完成会调用 */
    - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView;
    /* 地图加载失败会调用 */
    - (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error;
    /* 用户位置发生改变会调用 */
    - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;
    /* 显示区域改变会调用 */
    - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
    /* 点击选中大头针时会调用 */
    - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view;
    /* 取消选中大头针时会调用 */
    - (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view;
    /* 显示地图上的大头针,功能类似于UITableView的tableView:cellForRowAtIndexPath:方法 */
    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation;
    

    三、MapKit使用

    1. 首先添加头文件:

    #import <MapKit/MapKit.h>
    

    2. 初始化地图展示控件MKMapView

    - (void)initMapView{
        CGFloat x = 0;
        CGFloat y = 20;
        CGFloat width = self.view.frame.size.width;
        CGFloat height = self.view.frame.size.height;
        //创建MKMapView,设置控件视图大小
        MKMapView *mapView = [[MKMapView alloc] initWithFrame:CGRectMake(x, y, width, height)];
        //设置地图类型
        mapView.mapType = MKMapTypeStandard;
        //设置代理
        mapView.delegate = self;
        [self.view addSubview:mapView];
        self.mapView = mapView;
    }
    

    3. 用户位置跟踪

    在iOS8之前,实现这个功能只需要:
    1. 设置用户跟踪模式
    2. mapView:DidUpdateUserLocation:代理方法中设置地图中心和显示范围
    在iOS8之后,用法稍有不同:
    1. 必须按照前面的定位章节的,获取前台或者前后台的定位服务授权,下面是链接:
      iOS学习笔记19-地图(一)定位CoreLocation
    2. 不需要进行中心点的指定,默认会将当前位置设置为中心点并自动显示区域范围
    3. 只有定位到当前位置后mapView:DidUpdateUserLocation:代理方法才会调用
    - (void)viewDidLoad {
        [super viewDidLoad];
        //获取定位服务授权
        [self requestUserLocationAuthor];
        //初始化MKMapView
        [self initMapView];
    }
    - (void)requestUserLocationAuthor{
        //如果没有获得定位授权,获取定位授权请求
        self.locationM = [[CLLocationManager alloc] init];
        if ([CLLocationManager locationServicesEnabled]) {
            if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {
                [self.locationM requestWhenInUseAuthorization];
            }
        }
    }
    - (void)initMapView{
        CGFloat x = 0;
        CGFloat y = 20;
        CGFloat width = self.view.frame.size.width;
        CGFloat height = self.view.frame.size.height;
        //创建MKMapView对象
        MKMapView *mapView = [[MKMapView alloc] initWithFrame:CGRectMake(x, y, width, height)];
        //设置地图类型
        mapView.mapType = MKMapTypeStandard;
        //设置用户跟踪模式
        mapView.userTrackingMode = MKUserTrackingModeFollow;
        mapView.delegate = self;
        [self.view addSubview:mapView];
        self.mapView = mapView;
    }
    #pragma mark - MKMapViewDelegate
    /* 更新用户位置会调用 */
    - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
        CLLocation *location = userLocation.location;
        CLLocationCoordinate2D coordinate = location.coordinate;
        NSLog(@"经度:%f,纬度:%f",coordinate.latitude,coordinate.longitude);
    }
    

    用户位置跟踪

    4. 添加大头针

    MapKit没有自带的大头针,只有大头针协议MKAnnotation,我们需要自定义大头针:

    1. 创建一个继承NSObject的类
    2. 实现MKAnnotation协议
    3. 必须创建一个属性,用于存储大头针位置
    @property (nonatomic) CLLocationCoordinate2D coordinate;
    
    下面就是我简单创建的LTAnnotation类:
    #import <Foundation/Foundation.h>
    #import <MapKit/MapKit.h>
    
    @interface LTAnnotation : NSObject <MKAnnotation>
    /* 必须创建的属性 */
    @property (nonatomic) CLLocationCoordinate2D coordinate;
    /* 可选的属性 */
    @property (nonatomic, copy) NSString *title;
    @property (nonatomic, copy) NSString *subtitle;
    /* 自定义的属性 */
    @property (nonatomic, strong) UIImage *icon;
    @end
    
    @implementation LTAnnotation
    @end
    
    下面是实际的使用:
    - (void)viewDidLoad {
        [super viewDidLoad];
        //请求定位授权
        [self requestUserLocationAuthor];
        //初始化MKMapView
        [self initMapView];
        //添加大头针
        [self addAnnotationsToMapView];
    }
    - (void)addAnnotationsToMapView{
        CLLocationCoordinate2D location1 = CLLocationCoordinate2DMake(22.54, 114.02);
        //创建大头针
        LTAnnotation *annotation = [[LTAnnotation alloc] init];
        annotation.title = @"执着";
        annotation.subtitle = @"执着哥开的店";
        annotation.coordinate = location1;
        annotation.icon = [UIImage imageNamed:@"red"];
        //添加大头针
        [self.mapView addAnnotation:annotation1];
    }
    

    大头针在地图上的显示

    点击大头针显示

    5. 自定义大头针视图

    上面的大头针样子是不是很丑,那是MKMapView的默认样式大头针视图MKAnnotationView,我们先来了解下它的常用属性:

    @property (nonatomic, strong) id<MKAnnotation> annotation;/*< 大头针数据 */
    @property (nonatomic, strong) UIImage *image;/*< 大头针的图标 */
    @property (nonatomic, readonly) NSString *reuseIdentifier;/*< 大头针的唯一标示 */
    @property (nonatomic) CGPoint calloutOffset;/*< 弹出视图的偏移 */
    @property (nonatomic) BOOL selected;/*< 是否选中 */
    @property (nonatomic) BOOL canShowCallout;/*< 是否能点击弹出视图 */
    @property (nonatomic, strong) UIView *leftCalloutAccessoryView;/*< 弹出视图左边的视图 */
    @property (nonatomic, strong) UIView *rightCalloutAccessoryView;/*< 弹出视图右边的视图 */
    
    下面是通过设置MKAnnotationView的属性,自定义大头针视图:
    /* 每当大头针显示在可视界面上时,就会调用该方法,用户位置的蓝色点也是个大头针,也会调用 */
    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
    {
        if ([annotation isKindOfClass:[LTAnnotation class]]) {
            LTAnnotation *annotationLT = (LTAnnotation *)annotation;
            //类似于UITableViewCell的重用机制,大头针视图也有重用机制
            static NSString *key = @"AnnotationIdentifier";
            MKAnnotationView *view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:key];
            if (!view) {
                view = [[MKAnnotationView alloc] initWithAnnotation:annotation
                                                    reuseIdentifier:key];
            }
            //设置大头针数据
            view.annotation = annotation;
            //自定义大头针默认是NO,表示不能弹出视图,这里让大头针可以点击弹出视图
            view.canShowCallout = YES;
            //设置大头针图标
            view.image = annotationLT.icon;
            //设置弹出视图的左边视图
            UIImage *leftImage = [UIImage imageNamed:@"cafeIcon"];
            UIImageView *leftView = [[UIImageView alloc] initWithImage: leftImage];
            leftView.bounds = CGRectMake(0, 0, 50, 50);
            view.leftCalloutAccessoryView = leftView;
            //设置弹出视图的右边视图
            UIImage *rightImage = [UIImage imageNamed:@"cafeRight"];
            UIImageView *rightView = [[UIImageView alloc] initWithImage: rightImage];
            rightView.bounds = CGRectMake(0, 0, 50, 50);
            view.rightCalloutAccessoryView = rightView;
            return view;
        }
        //返回nil,表示显示默认样式
        return nil;
    }
    

    改变默认样式的大头针视图

    四、扩展--自定义大头针弹出详情视图

    如果你去关注下一些地图应用,会发现他们的弹出视图和我们的完全不一样,那是怎么实现的呢?

    实际上那不是弹出视图,那是个大头针,只是这个大头针做得和弹出视图很像而已。

    实现思路:
    1. 当点击普通的大头针时,移除地图上其他的详情大头针,添加当前大头针的详情大头针
    2. 当普通大头针取消选中时,移除地图上所有的详情大头针
    3. mapView:viewForAnnotation:方法中设置普通大头针视图和详情大头针视图
    下面是实现的部分代码【实现效果比较随便,见谅】:
    #pragma mark - 地图控件代理方法
    /* 显示大头针时调用,注意方法中的annotation参数是即将显示的大头针对象 */
    -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
        //由于当前位置的标注也是一个大头针,所以此时需要判断,此代理方法返回nil使用默认大头针视图
        if ([annotation isKindOfClass:[LTAnnotation class]]) {
            static NSString *key1 = @"AnnotationKey1";
            MKAnnotationView *annotationView = [_mapView dequeueReusableAnnotationViewWithIdentifier:key1];
            //如果缓存池中不存在则新建
            if (!annotationView) {
                annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
                                                              reuseIdentifier:key1];
                annotationView.canShowCallout = NO;//不允许弹出视图,但可以被选中
            }
            //重新设置此类大头针视图的大头针模型(因为有可能是从缓存池中取出来的,位置是放到缓存池时的位置)
            annotationView.annotation = annotation;
            annotationView.image = ((LTAnnotation *)annotation).icon;//设置大头针视图的图片
            return annotationView;
        }else if([annotation isKindOfClass:[LTCalloutAnnotation class]]){
            static NSString *key2 = @"AnnotationCallOutKey2";
            MKAnnotationView *calloutView = [_mapView dequeueReusableAnnotationViewWithIdentifier:key2];
            //如果缓存池中不存在则新建
            if (!calloutView) {
                calloutView = [[MKAnnotationView alloc] initWithAnnotation:annotation
                                                           reuseIdentifier:key2];
                calloutView.canShowCallout = NO;//不允许弹出视图,但可以被选中
            }
            //对于作为弹出详情视图的自定义大头针视图无弹出交互功能,在其中可以自由添加其他视图
            calloutView.annotation = annotation;
            //设置详情大头针的偏移位置
            calloutView.centerOffset = CGPointMake(-50, -80);
            [self calloutAddSubView:calloutView];
            return calloutView;
        } else {
            return nil;
        }
    }
    

    上面我的LTCalloutAnnotation和LTAnnotation实际上是只是类名不同而已,属性都一样。

    #pragma mark 添加弹出视图的子控件,这里我就很随便了,你可以搞得好看点
    - (void)calloutAddSubView:(MKAnnotationView *)calloutView
    {
        //添加背景
        UIView *background = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)];
        background.backgroundColor = [UIColor whiteColor];
        background.layer.borderWidth = 5;
        background.layer.borderColor = [UIColor blueColor].CGColor;
        [calloutView addSubview:background];
        //添加图片
        UIImage *image = [UIImage imageNamed:@"cafeRight"];
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        imageView.frame = CGRectMake(5, 5, 50, 50);
        [calloutView addSubview:imageView];
        //添加一个红色方块
        UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(60, 5, 35, 40)];
        subview.backgroundColor = [UIColor redColor];
        [calloutView addSubview:subview];
    }
    
    #pragma mark 选中大头针时触发
    //点击一般的大头针KCAnnotation时添加一个大头针作为所点大头针的弹出详情视图
    -(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
        if ([view.annotation isKindOfClass:[LTAnnotation class]]) {
            LTAnnotation *annotation = view.annotation;
            //点击一个大头针时移除其他弹出详情视图
            [self removeCalloutAnnotation];
            //添加详情大头针
            LTCalloutAnnotation *callout = [[LTCalloutAnnotation alloc] init];
            callout.icon = annotation.icon;
            callout.title = annotation.title;
            callout.subtitle = annotation.subtitle;
            callout.coordinate = annotation.coordinate;
            [self.mapView addAnnotation:callout];
        }
    }
    #pragma mark 取消选中时触发
    -(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{
        [self removeCalloutAnnotation];
    }
    #pragma mark 移除所用详情大头针
    -(void)removeCalloutAnnotation{
        [self.mapView.annotations enumerateObjectsUsingBlock:^(id obj,NSUInteger idx,BOOL *stop){
            if ([obj isKindOfClass:[LTCalloutAnnotation class]]) {
                [_mapView removeAnnotation:obj];
            }
        }];
    }
    

    自定义弹出视图的效果图

    这个自定义弹出详情视图,我做的比较简陋,我主要是为了好说明具体是怎么实现的,你可以把弹出界面做的好看点,顺便把一些大头针视图进行下封装,那一切就很完美了,O(∩_∩)O哈!这种实现是很低效的,每次都需要遍历所有的大头针,从中找到详情大头针,需要优化的地方很多,可以自己去想着优化。

    如果有什么意见请在下方评论区写出来,求关注,求打赏,O(∩_∩)O哈!
  • 相关阅读:
    LintCode "Maximum Gap"
    LintCode "Wood Cut"
    LintCode "Expression Evaluation"
    LintCode "Find Peak Element II"
    LintCode "Remove Node in Binary Search Tree"
    LintCode "Delete Digits"
    LintCode "Binary Representation"
    LeetCode "Game of Life"
    LintCode "Coins in a Line"
    LintCode "Word Break"
  • 原文地址:https://www.cnblogs.com/liutingIOS/p/5383063.html
Copyright © 2011-2022 走看看