iOS定位相关功能开发经验总结

1. Core Location 与 MapKit

1.1 Core Location —— 用于获取设备当前地理位置信息与朝向

  • 初始化与获取授权
    后台定位需要在Info.plist中添加对应键值

    Info.plist

    工厂环境下在组件配置中写入

    
      
        
          location
          ]]>
        
      
    
    

    初始化

    self.locationManager = [[CLLocationManager] alloc] init];
    self.locationManager.delegate = self;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
    self.locationManager.distanceFilter = 10;
    self.locationManager.pausesLocationUpdatesAutomatically = NO;
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) {
        self.locationManager.allowsBackgroundLocationUpdates = YES;
    }
    

    CLLocationManager初始化后,以及app的授权状态改变时,locationManager: didChangeAuthorizationStatus:会被调用。

    - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
        if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
            if (status == kCLAuthorizationStatusNotDetermined) {
                [self.locationManager requestAlwaysAuthorization];
            } else if (status != kCLAuthorizationStatusRestricted && status != kCLAuthorizationStatusDenied) {
                [self.locationManager startUpdatingLocation]; //开始获取GPS位置
                if ([CLLocationManager headingAvailable]) {
                    [self.locationManager startUpdatingHeading]; //开始获取设备朝向
                }
            } else {
                //无授权处理
            }
        }
    }
    
    typedef NS_ENUM(int, CLAuthorizationStatus) {
      kCLAuthorizationStatusNotDetermined = 0,
      kCLAuthorizationStatusRestricted,
      kCLAuthorizationStatusDenied,
      kCLAuthorizationStatusAuthorizedAlways,
      kCLAuthorizationStatusAuthorizedWhenInUse,
      kCLAuthorizationStatusAuthorized //只在macOS下使用
    };
    
iOS定位相关功能开发经验总结_第1张图片


  • 获得位置与朝向 CLLocationManagerDelegate
    - (void)locationManager:(CLLocationManager *)manager 
          didUpdateLocations:(NSArray *)locations {
    }
    
    - (void)locationManager:(CLLocationManager *)manager
          didUpdateHeading:(CLHeading *)newHeading {
    }
    
    这里获得的经纬度属于WGS84坐标系,而中国使用的是加密后的GCJ02坐标系,需要进行换算,具体方法参见这里。

  • Region Monitoring 监测用户进入或离开特定地理区域
    在iOS中,系统自动对用户的区域变化进行监测,在用户进入或离开我们定义的区域时,相应代理方法会执行。如果事件发生时我们的app并没有运行,系统会在后台唤醒我们的app。可以通过launchOptions字典中的UIApplicationLaunchOptionsKey来判断我们的app是否是由Region Monitoring唤醒的。同一个app最多只能同时监测20个region。


    使用步骤
    1. 创建 CLCircularRegion 对像,用于确定监测的区域。只能为圆形,官方不支持多边形区域。
    2. 注册区域,iOS会持续监测区域,直到我们的代码中停止区域监测,或者手机重启。
      [self.locationManager startMonitoringForRegion:region];
      
    3. 实现 locationManager:didEnterRegion:locationManager:didExitRegion: 代理方法
    4. AppDelegate.m文件的application:didFinishLaunchingWithOptions:函数中判断程序是否由Region Monitoring唤醒,如果是,进行处理。

  • Geocoding 坐标与实际街道名之间的转换
    • 前一个请求未完成时,后续请求会失败。

    • 苹果服务器会对每个app的请求频率做限制,超过限制之后的请求会失败。

    • 使用的坐标为地图提供的坐标系

    • 坐标转街道

      self.geocoder = [CLGeocoder new];
      [self.geocoder reverseGeocodeLocation:location 
                        completionHandler:^(NSArray *placemarks, NSError *error) {
      }];
      
    系统进行异步网络请求,completionHandler会在主线程执行。
    iOS定位相关功能开发经验总结_第2张图片
    • 街道转坐标
    - (void)geocodeAddressDictionary:(NSDictionary *)addressDictionary completionHandler:(CLGeocodeCompletionHandler)completionHandler;
    - (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;
    - (void)geocodeAddressString:(NSString *)addressString inRegion:(nullable CLRegion *)region completionHandler:(CLGeocodeCompletionHandler)completionHandler;
    


1.2 MapKit —— 在app中显示地图,在地图上显示景点,添加标记(Annotation),获取导航路径等

  • MKMapView (UIView的子类)
    • 不需要定位用户位置时

      MKMapView *mapView = [MKMapView new];
      mapView.delegate = self;
      [self.view addSubview:mapView];
      [mapView mas_makeConstraints:^(MASConstraintMaker *make) {
          make.edges.equalTo(self.view);
      }];
      
      iOS定位相关功能开发经验总结_第3张图片
    • 需要定位用户位置时

      1. 创建CLLocationManager申请权限
      2. 在地图上显示用户位置
        self.mapView.showsUserLocation = YES;
        
        也可以通过实现MKMapViewDelegate的代理方法获取用户坐标
        - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
            static BOOL didBeginInitialize = NO;
            if (!didBeginInitialize) {
                didBeginInitialize = YES;
                MKCoordinateSpan span = MKCoordinateSpanMake(0.02, 0.02);
                MKCoordinateRegion region = MKCoordinateRegionMake(userLocation.location.coordinate, span);
                [self.mapView setRegion:region animated:NO];
            }
        }
        
iOS定位相关功能开发经验总结_第4张图片
  • MKAnnotation 与 MKAnnotationView

    • 什么是Annotation


      iOS定位相关功能开发经验总结_第5张图片
    • 添加方法

      MKPointAnnotation *annotation = [MKPointAnnotation new];
      annotation.title = @"title";
      annotation.coordinate = CLLocationCoordinate2DMake(latitude,longitude);
      [self.mapView addAnnotation:annotation];
      
      iOS定位相关功能开发经验总结_第6张图片
      点击后的气泡中显示title
    • 如何定制

      - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation {
          if (annotation == self.mapView.userLocation) {
            //用户位置view
            MKAnnotationView *annotationView = [[MKAnnotationView  alloc] initWithAnnotation:annotation reuseIdentifier:nil];
            // ...
            return annotationView;
          } else { 
            //其他Annotation
          }
      }
      

      返回nil则会使用默认样式


      iOS定位相关功能开发经验总结_第7张图片
      自定义的annotationView
    • 修改已生成的annotationView,以及动效编写

      • annotationannotationView一一对应,修改annotation的属性值,对应的annotationView会有相应变化。
      • 系统会自动缓存已创建的annotationView,手动调用
        [self.mapView viewForAnnotation:annotation]时,如果annotation对应的annotationView已创建过,则不会创建新的annotationView,而是返回已缓存的。
      [self.mapView layoutIfNeeded];
      MKAnnotationView *annotationView = [self.mapView viewForAnnotation:annotation];
      // ...相关视图处理代码
      [UIView animateWithDuration:0.3 animations:^{
          [self.mapView layoutIfNeeded];
      }];
      


  • 区域标记 MKOverlay 与 MKOverlayRenderer

    iOS定位相关功能开发经验总结_第8张图片
    圆形overlay

    iOS定位相关功能开发经验总结_第9张图片
    多边形overlay

    圆形区域

    MKCircle *circle = [MKCircle circleWithCenterCoordinate:coordinate radius:1000];
    [self.mapView addOverlay:circle level:MKOverlayLevelAboveLabels];
    

    多边形区域

    CLLocationCoordinate2D *mapPointArray = (CLLocationCoordinate2D *)malloc(sizeof(CLLocationCoordinate2D) * count);
    for (NSInteger i = 0; i < N; i ++) {
        mapPointArray[i] = CLLocationCoordinate2DMake(latitude, longitude);
    }
    MKPolygon *polygon = [MKPolygon polygonWithCoordinates:mapPointArray count:count];
    [self.mapView addOverlay:polygon level:MKOverlayLevelAboveLabels];
    // 记得释放mapPointArray
    

    区域层级ENUM

    typedef NS_ENUM(NSInteger, MKOverlayLevel) {
      MKOverlayLevelAboveRoads = 0, // note that labels include shields and point of interest icons.
      MKOverlayLevelAboveLabels
    } NS_ENUM_AVAILABLE(10_9, 7_0) __TVOS_AVAILABLE(9_2) __WATCHOS_PROHIBITED;
    

    代理中返回对应视图

    - (MKOverlayRenderer *)mapView:(MKMapView *)map rendererForOverlay:(nonnull id)overlay {
        if ([overlay isKindOfClass:[MKCircle class]]) {
            MKCircleRenderer *circleRenderer = [[MKCircleRenderer alloc] initWithOverlay:overlay];
            circleRenderer.strokeColor = [[UIColor apf_colorWithHexString:@"ff9d2a"] colorWithAlphaComponent:0.3];
            circleRenderer.lineWidth = 3;
            circleRenderer.fillColor = [[LBSSLHelper colorWithKey:@"color_19"] colorWithAlphaComponent:0.25];
            return circleRenderer;
          
        } else if ([overlay isKindOfClass:[MKPolygon class]]) {
            MKPolygonRenderer *polygonRenderer = [[MKPolygonRenderer alloc] initWithPolygon:overlay];
            polygonRenderer.strokeColor = [[UIColor apf_colorWithHexString:@"ff9d2a"] colorWithAlphaComponent:0.3];
            polygonRenderer.lineWidth = 3;
            polygonRenderer.fillColor = [[LBSSLHelper colorWithKey:@"color_19"] colorWithAlphaComponent:0.25];
            return polygonRenderer;
        }
    }
    
  • 两点间路线 MKDirectionsRequest

    iOS定位相关功能开发经验总结_第10张图片
    MKDirectionsRequest *directionsRequest = [MKDirectionsRequest new];
    [directionsRequest setTransportType:MKDirectionsTransportTypeWalking];
    [directionsRequest setSource:[[MKMapItem alloc] initWithPlacemark:originPlacemark]];
    [directionsRequest setDestination:[[MKMapItem alloc] initWithPlacemark:destinationPlacemark]];
    [directionsRequest setRequestsAlternateRoutes:NO];
    MKDirections *direction = [[MKDirections alloc] initWithRequest:directionsRequest];
    
    [direction calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {
        if (!error) {
            //response中还有换乘建议、预计耗时等其他信息
            MKRoute *route = [response.routes firstObject];
            if (route) {
                [self.mapView addOverlay:route.polyline level:MKOverlayLevelAboveLabels];
            }
        }
    }];
    
    - (MKOverlayRenderer *)mapView:(MKMapView *)map rendererForOverlay:(nonnull id)overlay {
        if ([overlay isKindOfClass:[MKPolyline class]]) {
            MKPolylineRenderer *polylineRenderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
            polylineRenderer.lineWidth = 4;
            polylineRenderer.strokeColor = [LBSSLHelper colorWithKey:@"color_14"];
            return polylineRenderer;
        }
    }
    

2. 遇到的问题

  1. 单元测试报错



    解决方法:设置单元测试的环境变量,跳过属性设置。


    iOS定位相关功能开发经验总结_第11张图片

    iOS定位相关功能开发经验总结_第12张图片
  2. 工厂打包时的权限问题
    解决方法:使用子组件
    
      
        
      
    
    
  3. 请求后台运行权限上架被拒
    解决方法:与苹果审核人员沟通。

你可能感兴趣的:(iOS定位相关功能开发经验总结)