1. Core Location 与 MapKit
1.1 Core Location —— 用于获取设备当前地理位置信息与朝向
-
初始化与获取授权
Info.plist
后台定位需要在Info.plist中添加对应键值
工厂环境下在组件配置中写入
<ios> <meta-datas> <meta-data xpath="//plist/dict" key="UIBackgroundModes" type="array" file="/ComponentAppBase/Info.plist"> <![CDATA[ <string>location</string> ]]> </meta-data> </meta-datas> </ios>
初始化
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下使用 };
- 获得位置与朝向 CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { }
这里获得的经纬度属于WGS84坐标系,而中国使用的是加密后的GCJ02坐标系,需要进行换算,具体方法参见这里。- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { }
- Region Monitoring 监测用户进入或离开特定地理区域
在iOS中,系统自动对用户的区域变化进行监测,在用户进入或离开我们定义的区域时,相应代理方法会执行。如果事件发生时我们的app并没有运行,系统会在后台唤醒我们的app。可以通过launchOptions
字典中的UIApplicationLaunchOptionsKey
来判断我们的app是否是由Region Monitoring唤醒的。同一个app最多只能同时监测20个region。
使用步骤- 创建 CLCircularRegion 对像,用于确定监测的区域。只能为圆形,官方不支持多边形区域。
- 注册区域,iOS会持续监测区域,直到我们的代码中停止区域监测,或者手机重启。
[self.locationManager startMonitoringForRegion:region];
- 实现
locationManager:didEnterRegion:
和locationManager:didExitRegion:
代理方法 - 在
AppDelegate.m
文件的application:didFinishLaunchingWithOptions:
函数中判断程序是否由Region Monitoring唤醒,如果是,进行处理。
- Geocoding 坐标与实际街道名之间的转换
-
前一个请求未完成时,后续请求会失败。
-
苹果服务器会对每个app的请求频率做限制,超过限制之后的请求会失败。
-
使用的坐标为地图提供的坐标系
-
坐标转街道
self.geocoder = [CLGeocoder new]; [self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { }];
completionHandler
会在主线程执行。
- 街道转坐标
- (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); }];
-
需要定位用户位置时
- 创建
CLLocationManager
申请权限 - 在地图上显示用户位置
也可以通过实现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]; } }
- 创建
-
-
MKAnnotation 与 MKAnnotationView
-
什么是Annotation
-
添加方法
点击后的气泡中显示titleMKPointAnnotation *annotation = [MKPointAnnotation new]; annotation.title = @"title"; annotation.coordinate = CLLocationCoordinate2DMake(latitude,longitude); [self.mapView addAnnotation:annotation];
-
如何定制
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation { if (annotation == self.mapView.userLocation) { //用户位置view MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil]; // ... return annotationView; } else { //其他Annotation } }
返回nil则会使用默认样式
自定义的annotationView -
修改已生成的
annotationView
,以及动效编写- �
annotation
与annotationView
一一对应,修改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
圆形overlay
多边形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<MKOverlay>)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
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<MKOverlay>)overlay { if ([overlay isKindOfClass:[MKPolyline class]]) { MKPolylineRenderer *polylineRenderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay]; polylineRenderer.lineWidth = 4; polylineRenderer.strokeColor = [LBSSLHelper colorWithKey:@"color_14"]; return polylineRenderer; } }
2. 遇到的问题
-
单元测试报错
解决方法:设置单元测试的环境变量,跳过属性设置。
- 工厂打包时的权限问题
解决方法:使用子组件<property type="bool" name="showLocationShare" desc_true="需要位置分享" desc_false="不需要位置分享" displayName="是否需要位置分享" value="false"> <dependency> <component namespace="com.nd.social" name="lbs-share-location"/> </dependency> </property>
- 请求后台运行权限上架被拒
解决方法:与苹果审核人员沟通。
网友评论