iOS开发-地图开发(CoreLocation和MapKit))

地图(CoreLocation和MapKit)

  • CoreLocation框架

    • CoreLocation
    • CLLocation
    • ios地图开发的隐私保护(如何添加授权)
    • CLGeocoder
    • CLPlacemark
  • MapKit框架

    • MKMapView
    • MKCoordinateRegion
    • MKMapView的代理
    • MKUserLocation
    • 大头针
    • annotation
    • 自定义大头针
    • MKAnnotationView
    • MKPinAnnotationView
    • MKMapItem调用系统APP进行导航
    • MKMapCamera地图街景
    • MKMapSnapshotter地图截图

2大框架

  • CoreLocation:用于地理定位,地理编码区域监听等(着重功能实现)
  • MapKit:用于地图展示,例如大头针,路线,覆盖层展示等(着重界面展示)

CoreLocation框架的使用

CLLocationManager

  • CoreLocation框架中使用CLLocationManager对象来做用户定位
  • 创建(初始化)
CLLocationManager *lM = [[CLLocationManager alloc] init];

  • 常用属性
    • 每隔多少米定位一次
      @property(assign, nonatomic) CLLocationDistance distanceFilter;
    • 定位精确度(越精确就越耗电)
      @property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;
    • 朝向改变时,每隔多少度调用一次
      @property(assign, nonatomic) CLLocationDegrees headingFilter
         每隔多米定位一次
       _lM.distanceFilter = 100;
       /**
          kCLLocationAccuracyBestForNavigation // 最适合导航
          kCLLocationAccuracyBest; // 最好的
          kCLLocationAccuracyNearestTenMeters; // 10m
          kCLLocationAccuracyHundredMeters; // 100m
          kCLLocationAccuracyKilometer; // 1000m
          kCLLocationAccuracyThreeKilometers; // 3000m
        */
       // 精确度越高, 越耗电, 定位时间越长
       _lM.desiredAccuracy = kCLLocationAccuracyBest;
       

  • 常用方法
    • 开始更新用户位置- (void)startUpdatingLocation;

      • 当调用了startUpdatingLocation方法后,就开始不断地请求、刷新用户的位置,一旦请求到用户位置就会调用代理的下面方法
        -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;

      • locations参数里面装着CLLocation对象

    • 停止更新用户位置- (void) stopUpdatingLocation;

    • 判断定位功能是否可用(为了严谨起见,最好在使用定位功能前进行判断)+ (BOOL)locationServicesEnabled;

    • 监听设备朝向 -(void)startUpdatingHeading(如试例代码中的指南针的实现)

    • 区域监听(监听进出某个区域) -(void)requestStateForRegion:region;

    //使用位置管理者,开始更新用户位置
    // 默认只能在前台获取用户位置,
    // 勾选后台模式 location updates
    [self.lM startUpdatingLocation];

    //监听设备朝向
    [self.lM startUpdatingHeading];
   - 区域监听(监听进出某个区域)
   [self.lM requestStateForRegion:region];
  • 代理方法
    • 跟新位置后调用- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
    • 授权状态改变时调用-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
    • 获取手机朝向时调用
      -(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
    • 区域监听的代理方法
      • 进入区域时调用:-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
      • 离开区域时调用:-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
      • 监听是否在某个区域的状态:-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state
/**
*  更新到位置之后调用
*
*  @param manager   位置管理者
*  @param locations 位置数组
*/
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
   NSLog(@"定位到了");
   
   // 拿到位置,做一些业务逻辑操作
   
   
   // 停止更新
//    [manager stopUpdatingLocation];
}
/**
 *  授权状态发生改变时调用
 *
 *  @param manager 位置管理者
 *  @param status  状态
 */
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status) {
            // 用户还未决定
        case kCLAuthorizationStatusNotDetermined:
        {
            NSLog(@"用户还未决定");
            break;
        }
            // 问受限
        case kCLAuthorizationStatusRestricted:
        {
            NSLog(@"访问受限");
            break;
        }
            // 定位关闭时和对此APP授权为never时调用
        case kCLAuthorizationStatusDenied:
        {
            // 定位是否可用(是否支持定位或者定位是否开启)
            if([CLLocationManager locationServicesEnabled])
            {
                NSLog(@"定位开启,但被拒");
            }else
            {
                NSLog(@"定位关闭,不可用");
            }
//            NSLog(@"被拒");
            break;
        }
            // 获取前后台定位授权
        case kCLAuthorizationStatusAuthorizedAlways:
            //        case kCLAuthorizationStatusAuthorized: // 失效,不建议使用
        {
            NSLog(@"获取前后台定位授权");
            break;
        }
            // 获得前台定位授权
        case kCLAuthorizationStatusAuthorizedWhenInUse:
        {
            NSLog(@"获得前台定位授权");
            break;
        }
        default:
            break;
    }
}
/**
 *  简易指南针的实现
 *  手机朝向改变时调用
 *
 *  @param manager    位置管理者
 *  @param newHeading 朝向对象
 */
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    /**
     *  CLHeading 
     *  magneticHeading : 磁北角度
     *  trueHeading : 真北角度
     */
    
    NSLog(@"%f", newHeading.magneticHeading);
    
    CGFloat angle = newHeading.magneticHeading;
    
    // 把角度转弧度
    CGFloat angleR = angle / 180.0 * M_PI;
    
    // 旋转图片(指南针图片)
    [UIView animateWithDuration:0.25 animations:^{
        self.compassView.transform = CGAffineTransformMakeRotation(-angleR);
    }]; 
}

//区域监听的代理方法
// 进入区域
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
   NSLog(@"进入区域--%@", region.identifier);
}

// 离开区域
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
   NSLog(@"离开区域--%@", region.identifier);
}

//监听是否在某个区域的状态
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
   //state的值
   //CLRegionStateUnknown,
   //CLRegionStateInside,
   //CLRegionStateOutside
   NSLog(@"%zd", state);   
}

CLLocation

  • CLLocation用来表示某个位置的地理信息,比如经纬度,海拔等等
  • 常用属性
    • @property(readonly, nonatomic) CLLocationCoordinate2D coordinate;
      经纬度
    • @property(readonly, nonatomic) CLLocationDistance altitude;
      海拔
    • @property(readonly, nonatomic) CLLocationDirection course;
      路线,航向(取值范围是0.0° ~ 359.9°,0.0°代表真北方向)
    • @property(readonly, nonatomic) CLLocationSpeed speed;
      移动速度(单位是m/s)
  • 常用方法
    • 计算两个位置之间的距离:- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location

使用试例:根据移动方向打印出移动的方向和距离

/**
 *  CLLocationManager对象的代理方法,当用户位置改变的时候调用
 *  更新到位置之后调用
 *
 *  @param manager   位置管理者
 *  @param locations 位置数组
 * is kind of
 */
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
//    NSLog(@"定位到了");
    /**
     *  CLLocation 详解
     *  coordinate : 经纬度
     *  altitude : 海拔
     *  course : 航向
     *  speed ; 速度
     */

    CLLocation *location = [locations lastObject];
    
//    NSLog(@"%@", location);
    
    /**
     *  场景演示:打印当前用户的行走方向,偏离角度以及对应的行走距离,
        例如:”北偏东 30度  方向,移动了8米”
     */
    
    // 1. 获取方向偏向
    NSString *angleStr = nil;
    
    switch ((int)location.course / 90) {
        case 0:
            angleStr = @"北偏东";
            break;
        case 1:
            angleStr = @"东偏南";
            break;
        case 2:
            angleStr = @"南偏西";
            break;
        case 3:
            angleStr = @"西偏北";
            break;
            
        default:
            angleStr = @"跑沟里去了!!";
            break;
    }
    // 2. 偏向角度
    NSInteger angle = 0;
    angle = (int)location.course % 90;
    
    // 代表正方向
    if (angle == 0) {
        NSRange range = NSMakeRange(0, 1);
        angleStr = [NSString stringWithFormat:@"正%@", [angleStr substringWithRange:range]];
    }
    // 3.移动多少米
    double distance = 0;
    if(_oldL)
    {
        distance = [location distanceFromLocation:_oldL];
    }
    _oldL = location;
    // 4. 拼串 打印
    // 例如:”北偏东 30度  方向,移动了8米”
    NSString *noticeStr = [NSString stringWithFormat:@"%@%zd方向, 移动了%f米", angleStr, angle, distance];
    NSLog(@"%@", noticeStr);
}

ios地图开发用户隐私保护(授权)

ios6+

  • 当使用定位时,系统会自动弹出对话框让用户授权。
    - 一旦用户选择了不允许,意味着应用程序以后都无法使用定位。
  • 开发者可以在Info.plist中设置NSLocationUsageDescription说明定位的目的(Privacy - Location Usage Description)

ios8.0+

从ios8.0开始,苹果进一步加强了对用户隐私的保护。
  • 当app想访问用户的隐私时,系统不再自动弹出一个对话框让用户授权.
解决方案
  • (1)调用ios8.0的api。主动请求用户授权
    • -(void)requestAlwaysAuthorization // 请求允许在前后台都能获取用户位置的授权
    • -(void)requestWhenInUseAuthorization // 请求允许在前台获取用户位置的授权(注意:当设置为前台授权时,通过设置后台模式:location updates后 也可以后台获取定位信息,但是屏幕上方会出现蓝条)
  • (2)务必在info.plist文件中配置对应的键值, 否则以上请求授权的方法不生效
    • NSLocationAlwaysUsageDescription : 允许在前后台获取GPS的描述
    • NSLocationWhenInUseDescription : 允许在前台获取GPS的描述

ios9.0

  • ios9.0如果当前处于前台授权状态,默认是不可以后台获取用户位置。但可以设置以下属性为YES,就可以继续在后台获取位置,但是在屏幕上方会出现蓝条
    • @property(assign, nonatomic) BOOL allowsBackgroundLocationUpdates
    • 使用注意:必须设置对应的后台模式:location updates
  • ios9.0可以单次请求用户位置
    • -(void)requestLocation
    • -(void)locationManager:(nonnull CLLocationManager *)manager didUpdateLocations:(nonnull NSArray<CLLocation *> *)locations // 成功调用
    • -(void)locationManager:(nonnull CLLocationManager *)manager didFailWithError:(nonnull NSError *)error // 失败调用

CLGeocoder

  • 使用CLGeocoder可以完成“地理编码”和“反地理编码”
    • 地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)
    • 反地理编码:根据给定的经纬度,获得具体的位置信息
  • 对应方法
    • 地理编码方法-(void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;
    • 反地理编码方法-(void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;
  • CLGeocodeCompletionHandler:反地理编码完成时,就会调用CLGeocodeCompletionHandler
    • typedef void (^CLGeocodeCompletionHandler)(NSArray *placemarks, NSError *error);
      • error :当编码出错时(比如编码不出具体的信息)有值
      • placemarks :里面装着CLPlacemark对象

地理编码的实现

// 地理编码
- (IBAction)geoCoder {

    //当传入的值为0时直接返回
    if ([self.addressDetailTV.text length] == 0) {
        return;
    }

    // 地理编码方案一:直接根据地址进行地理编码(返回结果可能有多个,因为一个地点有重名)
    [self.geoC geocodeAddressString:self.addressDetailTV.text completionHandler:^(NSArray<CLPlacemark *> * __nullable placemarks, NSError * __nullable error) {
        // 包含区,街道等信息的地标对象
        CLPlacemark *placemark = [placemarks firstObject];
        // 城市名称
//        NSString *city = placemark.locality;
        // 街道名称
//        NSString *street = placemark.thoroughfare;
        // 全称
        NSString *name = placemark.name;
        self.addressDetailTV.text = [NSString stringWithFormat:@"%@", name];
        self.latitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude];
        self.longtitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude];
    }];
    // 地理编码方案二:根据地址和区域两个条件进行地理编码(更加精确)
//    [self.geoC geocodeAddressString:self.addressDetailTV.text inRegion:nil completionHandler:^(NSArray<CLPlacemark *> * __nullable placemarks, NSError * __nullable error) {
//        // 包含区,街道等信息的地标对象
//        CLPlacemark *placemark = [placemarks firstObject];
//        self.addressDetailTV.text = placemark.description;
//        self.latitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude];
//        self.longtitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude];
//    }];

    

    // 地理编码方案三:
//    NSDictionary *addressDic = @{
//                                 (__bridge NSString *)kABPersonAddressCityKey : @"北京",
//                                 (__bridge NSString *)kABPersonAddressStreetKey : @"棠下街"
//                                 };
//    [self.geoC geocodeAddressDictionary:addressDic completionHandler:^(NSArray<CLPlacemark *> * __nullable placemarks, NSError * __nullable error) {
//        CLPlacemark *placemark = [placemarks firstObject];
//        self.addressDetailTV.text = placemark.description;
//        self.latitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude];
//        self.longtitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude];
//    }];
}

反地理编码的实现

// 反地理编码
- (IBAction)decode {
    // 过滤空数据
    if ([self.latitudeTF.text length] == 0 || [self.longtitudeTF.text length] == 0) {
        return;
    }
    // 创建CLLocation对象
    CLLocation *location = [[CLLocation alloc] initWithLatitude:[self.latitudeTF.text doubleValue] longitude:[self.longtitudeTF.text doubleValue]];
    // 根据CLLocation对象进行反地理编码
    [self.geoC reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * __nullable placemarks, NSError * __nullable error) {
        // 包含区,街道等信息的地标对象
        CLPlacemark *placemark = [placemarks firstObject];
        // 城市名称
//        NSString *city = placemark.locality;
        // 街道名称
//        NSString *street = placemark.thoroughfare;
        // 全称
        NSString *name = placemark.name;
        self.addressDetailTV.text = [NSString stringWithFormat:@"%@", name];
    }];

}

CLPlacemark

  • CLPlacemark的字面意思是地标,封装详细的地址位置信息
  • 常用属性
    • @property (nonatomic, readonly) CLLocation *location;地理位置
    • @property (nonatomic, readonly) CLRegion *region;
      区域
    • @property (nonatomic, readonly) NSDictionary *addressDictionary;详细的地址信息
    • @property (nonatomic, readonly) NSString *name;
      地址名称
    • @property (nonatomic, readonly) NSString *locality;
      城市

MapKit框架的使用

MapKit通过MKMapView来显示地图

  • 常用属性
    • 地图配型:mapType

      • MKMapTypeStandard :普通地图(左图)
      • MKMapTypeSatellite :卫星云图 (中图)
      • MKMapTypeHybrid :混合模式(普通地图覆盖于卫星云图之上 )
      • MKMapTypeSatelliteFlyover: 3D立体卫星 (iOS9.0)
      • MKMapTypeHybridFlyover: 3D立体混合 (iOS9.0)
    • 操作项

      • 是否可缩放 zoomEnabled
      • 是否可滚动 scrollEnabled
      • 是否可旋转 rotateEnabled
    • 显示项

      • 是否显示指南针 showsCompass (iOS9.0)
      • 是否显示比例尺 showsScale (iOS9.0)
      • 是否显示交通 showsTraffic (iOS9.0)
      • 是否显示建筑 showsBuildings
    • 跟踪显示用户的位置(ios8-地图不会自动滚到用户所在的位置,ios8+地图会自动放大到合适比例,并显示出用户位置)

      • MKUserTrackingModeNone :不跟踪用户的位置
      • MKUserTrackingModeFollow :跟踪并在地图上显示用户的当前位置
      • MKUserTrackingModeFollowWithHeading :跟踪并在地图上显示用户的当前位置,地图会跟随用户的前进方向进行旋转
    • 设置地图显示的区域和位置

      • 设置地图的中心点位置
        • @property (nonatomic) CLLocationCoordinate2D centerCoordinate;
        • -(void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
      • 设置地图的显示区域
        • @property (nonatomic) MKCoordinateRegion region;
        • -(void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;

MKCoordinateRegion

  • MKCoordinateRegion是一个用来表示区域的结构体
typedef struct {
         CLLocationCoordinate2D center; // 区域的中心点位置
        MKCoordinateSpan span; // 区域的跨度
} MKCoordinateRegion;
  • MKCoordinateSpan
typedef struct {
    CLLocationDegrees latitudeDelta; // 纬度跨度
    CLLocationDegrees longitudeDelta; // 经度跨度
} MKCoordinateSpan;

MKMapView的代理

  • -(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;
    • 一个位置更改默认只会调用一次,不断监测用户的当前位置
    • 每次调用,都会把用户的最新位置(userLocation参数)传进来
  • -(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
    • 地图的显示区域即将发生改变的时候调用
  • -(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
    • 地图的显示区域已经发生改变的时候调用

MKUserLocation

  • @property (nonatomic, copy) NSString *title;
    显示在大头针上的标题

  • @property (nonatomic, copy) NSString *subtitle;
    显示在大头针上的子标题

  • @property (readonly, nonatomic) CLLocation *location;
    地理位置信息(大头针钉在什么地方?)

大头针

  • 添加大头针

    • 添加一个大头针
      -(void)addAnnotation:(id <MKAnnotation>)annotation;
    • 添加多个大头针
      -(void)addAnnotations:(NSArray *)annotations;
  • 移除大头针

    • 移除一个大头针
      -(void)removeAnnotation:(id <MKAnnotation>)annotation;

    • 移除多个大头针
      -(void)removeAnnotations:(NSArray *)annotations;

annotation

  • 大头针模型对象
#import <MapKit/MapKit.h>

@interface TestAnnotation : NSObject <MKAnnotation>
/** 坐标位置 */
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
/** 标题 */
@property (nonatomic, copy) NSString *title; 
/** 子标题 */
@property (nonatomic, copy) NSString *subtitle; 
@end

自定义大头针

  • 设置MKMapView代理
    • -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation;
      根据传进来的(id <MKAnnotation>)annotation参数创建并返回对应的大头针控件
    • 注意:如果返回nil,显示出来的大头针就采取系统的默认样式
      标识用户位置的蓝色发光圆点,它也是一个大头针,当显示这个大头针时,也会调用代理方法
      因此,需要在代理方法中分清楚(id <MKAnnotation>)annotation参数代表自定义的大头针还是蓝色发光圆点
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    // 判断annotation的类型
    if (![annotation isKindOfClass:[TestAnnotation class]]) return nil;
    
    // 创建MKAnnotationView
    static NSString *ID = @"tuangou";
    MKAnnotationView *annoView = [mapView dequeueReusableAnnotationViewWithIdentifier:ID];
    if (annoView == nil) {
        annoView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
        annoView.canShowCallout = YES;
    }

MKAnnotationView

地图上的大头针控件是MKAnnotationView
  • 属性
    • @property (nonatomic, strong) id <MKAnnotation> annotation;
      大头针模型
    • @property (nonatomic, strong) UIImage *image;
      显示的图片
    • @property (nonatomic) BOOL canShowCallout;
      是否显示标注
    • @property (nonatomic) CGPoint calloutOffset;
      标注的偏移量
    • @property (strong, nonatomic) UIView *rightCalloutAccessoryView;
      标注右边显示什么控件
    • @property (strong, nonatomic) UIView *leftCalloutAccessoryView;
      标注左边显示什么控件
    • @property (nonatomic, strong) UIView *detailCalloutAccessoryView
      标注下面显示什么控件(iOS9.0)

MKPinAnnotationView

MKPinAnnotationView是MKAnnotationView的子类

MKPinAnnotationView比MKAnnotationView多了2个属性

  • @property (nonatomic) MKPinAnnotationColor pinColor;
    大头针颜色
  • @property (nonatomic) BOOL animatesDrop;
    大头针第一次显示时是否从天而降

MKMapItem调用系统APP进行导航

// 根据两个地标对象进行调用系统导航
- (void)beginNavWithBeginPlacemark:(CLPlacemark *)beginPlacemark andEndPlacemark:(CLPlacemark *)endPlacemark
{
    // 创建起点:根据 CLPlacemark 地标对象创建 MKPlacemark 地标对象
    MKPlacemark *itemP1 = [[MKPlacemark alloc] initWithPlacemark:beginPlacemark];
    MKMapItem *item1 = [[MKMapItem alloc] initWithPlacemark:itemP1];

    // 创建终点:根据 CLPlacemark 地标对象创建 MKPlacemark 地标对象
    MKPlacemark *itemP2 = [[MKPlacemark alloc] initWithPlacemark:endPlacemark];
    MKMapItem *item2 = [[MKMapItem alloc] initWithPlacemark:itemP2];

    NSDictionary *launchDic = @{
                                // 设置导航模式参数
                                MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving,
                                // 设置地图类型
                                MKLaunchOptionsMapTypeKey : @(MKMapTypeHybridFlyover),
                                // 设置是否显示交通
                                MKLaunchOptionsShowsTrafficKey : @(YES),

                                };
    // 根据 MKMapItem 数组 和 启动参数字典 来调用系统地图进行导航
    [MKMapItem openMapsWithItems:@[item1, item2] launchOptions:launchDic];
}

MKMapCamera地图街景

    // 创建视角中心坐标
     CLLocationCoordinate2D center = CLLocationCoordinate2DMake(23.132931, 113.375924);
    // 创建3D视角
    MKMapCamera *camera = [MKMapCamera cameraLookingAtCenterCoordinate:center fromEyeCoordinate:CLLocationCoordinate2DMake(center.latitude + 0.001, center.longitude + 0.001) eyeAltitude:1];
    // 设置到地图上显示
    self.mapView.camera = camera;

MKMapSnapshotter地图截图

// 截图附加选项
    MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
    // 设置截图区域(在地图上的区域,作用在地图)
    options.region = self.mapView.region;
//    options.mapRect = self.mapView.visibleMapRect;

    // 设置截图后的图片大小(作用在输出图像)
    options.size = self.mapView.frame.size;
    // 设置截图后的图片比例(默认是屏幕比例, 作用在输出图像)
    options.scale = [[UIScreen mainScreen] scale];

    MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
    [snapshotter startWithCompletionHandler:^(MKMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
        if (error) {
            NSLog(@"截图错误:%@",error.localizedDescription);
        }else
        {
            // 设置屏幕上图片显示
            self.snapshootImageView.image = snapshot.image;
            // 将图片保存到指定路径(此处是桌面路径,需要根据个人电脑不同进行修改)
            NSData *data = UIImagePNGRepresentation(snapshot.image);
            [data writeToFile:@"/Users/wangshunzi/Desktop/snap.png" atomically:YES];
        }
    }];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容