iOS根据地图定位实现定点语音播报功能

首先看一下Demo的整体实现效果

项目展示.gif

Demo实现的几大块技能点:

1.地图展示线路、气泡展示点详情

2.语音合成部分

3.后台定位,计算点之间距离判断是否播报语音

下面分段讲述每个部分东西:

1.地图展示线路部分

其实做过地图方面应用的程序猿们应该都知道怎么做。本demo没有集成其他地图,用的是系统的地图,所以有点坑需要填。画线部分就不做赘述,基本上都是用MKPolyline画线并作为overlay添加进mapView里面,如果不会的话,网上百度会有一大堆文章介绍怎么去画线的。这里面唯一有点坑的就是,标注大头针还需要同时展示气泡这个操作,如下面的这个样式。


image.png

正常的我们在地图上展示气泡都不会同时展示出气泡中的内容的,所以这个就需要我们重写这个方法。需要重写两个类,一个是继承MKAnnotation的类:
.h文件:

  #import <Foundation/Foundation.h>
  #import <MapKit/MapKit.h>
  @interface MyAnnotation : NSObject<MKAnnotation>
  //显示标注的经纬度
  @property (nonatomic,readonly) CLLocationCoordinate2D coordinate;
  //标注的标题
  @property (nonatomic,copy,readonly) NSString * title;
  //标注的子标题
  @property (nonatomic,copy,readonly) NSString * subtitle;
  -(id)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates title:(NSString *)paramTitle
            subTitle:(NSString *)paramTitle;
  @end

.m文件

  #import "MyAnnotation.h"

  @implementation MyAnnotation
  -(id)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates title:(NSString *)paramTitle
            subTitle:(NSString *)paramSubitle
  {
      self = [super init];
      if(self != nil)
      {
          _coordinate = paramCoordinates;
          _title = paramTitle;
          _subtitle = paramSubitle;
      }
      return self;
  }
  @end

另一个就是重写一个继承MKAnnotationView的类
.h文件:

  #import <MapKit/MapKit.h>
  #import "MyAnnotation.h"

  @interface NewAnnotation : MKAnnotationView
  @property (nonatomic,strong) UILabel *label;
  @end

.m文件:

  #import "NewAnnotation.h"

  @implementation NewAnnotation

  - (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {

    //        在大头针旁边加一个label
    self.label = [[UILabel alloc]initWithFrame:CGRectMake(-40,-40,80, 20)];
    self.label.text = annotation.title;
    self.label.font = [UIFont systemFontOfSize:11];
    self.label.textColor = [UIColor orangeColor];
    self.label.backgroundColor = [UIColor whiteColor];
    self.label.textAlignment = NSTextAlignmentCenter;
    [self addSubview:self.label];

    UIImageView *myImageView = [[UIImageView alloc] initWithFrame:CGRectMake(-10,-20, 20, 26)];
    myImageView.image = [UIImage imageNamed:@"bubble_icon"];
    [self addSubview:myImageView];
}
return self;

  }


  @end

创建气泡也就不用说,就像下面这么简单就OK了

  CLLocationCoordinate2D coor = CLLocationCoordinate2DMake([lat_lonArray[1] doubleValue], [lat_lonArray[0] doubleValue]);
  MyAnnotation * myAnnotation = [[MyAnnotation alloc] initWithCoordinates:coor title:[NSString stringWithFormat:@"%d.%@",j+1,model.name] subTitle:@""];
 [weakSelf.mapView addAnnotation:myAnnotation];

代理方法:

  -(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
  {
MKAnnotationView * result = nil;
if([annotation isKindOfClass:[MKUserLocation class]])
{
    MKAnnotationView *iAnnotation=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"annotation2"];
    iAnnotation.image = [UIImage imageNamed:@"car_icon"];
    iAnnotation.canShowCallout=NO;
    return iAnnotation;
}

if([mapView isEqual:self.mapView] == NO)
{
    return result;
}
NewAnnotation *annotationView =
annotationView = [[NewAnnotation alloc]initWithAnnotation:annotation reuseIdentifier:@"otherAnnotationView"];
return annotationView;
  }

画线标打头阵、气泡这样就结束了。

2.语音合成部分

语音合成部分,我使用的是百度语音合成,虽然声音听上去没有讯飞语音那个美女声音好听,但是百度语音不管在线还是离线语音都可以用,每天免费的次数还多啊 ,不选这选啥呀。
百度语音合成怎么集成进项目里面,就不说了,毕竟人家百度写的已经很详细了,没有可赘述的。需要注意的是,因为这个demo需要做到后台定位和后台语音播报的功能,所以必须去修改下面的东西:


image.png

至于语音播报使用到麦克风和地图定位使用到定位权限的白名单设置问题,也不赘述了,这个都是比较简单的东西。

3.后台定位,计算点之间距离判断是否播报语音

后台定位功能仍然开启.png

这个蓝条,是app使用后台定位的效果,最主要的一句话就是下面这句:

if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) {
    self .locationManager.allowsBackgroundLocationUpdates = YES;
}

这句 self .locationManager.allowsBackgroundLocationUpdates = YES;是iOS9.0之后的API,需要判断下手机系统,如果是之前的系统,强行使用这句话的话,就会直接崩溃。
后台定位是使用CLLocationManagerDelegate更新位置的代理方法,而不是走MKMapView更新位置的代理方法。即

  -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations

而不是下面这个方法:

-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation

但是CLLocationManagerDelegate更新位置的代理方法有点坑,定位的经纬度是国外的地图的经纬度,与中国的经纬度有区别,偏差较大。所以需要转换成国内的经纬度,即火星座标。
网上有很多将系统地图经纬度转换成火星座标敬畏的文章,下面贴出我用的这个:
.h文件

  //
//  CLLocation+Sino.h
//
//  Created by [email protected] on 13-4-26.
//  火星座标系转换扩展
//
//  earth(国外 WGS84), mars(国内 GCJ-02), bearPaw(百度 BD-09) 座标系间相互转换
//  未包含 mars2earth. 需要这个可参考 http://xcodev.com/131.html

#import <CoreLocation/CoreLocation.h>

@interface CLLocation (Sino)

- (CLLocation*)locationMarsFromEarth;
//- (CLLocation*)locationEarthFromMars; // 未实现

- (CLLocation*)locationBearPawFromMars;
- (CLLocation*)locationMarsFromBearPaw;

@end

.m文件:

#import "CLLocation+Sino.h"

void transform_earth_2_mars(double lat, double lng, double* tarLat, double* tarLng);
void transform_mars_2_bear_paw(double lat, double lng, double* tarLat, double* tarLng);
void transform_bear_paw_2_mars(double lat, double lng, double* tarLat, double* tarLng);

@implementation CLLocation (Sino)

- (CLLocation*)locationMarsFromEarth;
{
double lat = 0.0;
double lng = 0.0;
transform_earth_2_mars(self.coordinate.latitude, self.coordinate.longitude, &lat, &lng);
return [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(lat, lng)
                                     altitude:self.altitude
                           horizontalAccuracy:self.horizontalAccuracy
                             verticalAccuracy:self.verticalAccuracy
                                       course:self.course
                                        speed:self.speed
                                    timestamp:self.timestamp];
}

- (CLLocation*)locationEarthFromMars;
{
// 二分法查纠偏文件
// http://xcodev.com/131.html
return nil;
}

- (CLLocation*)locationBearPawFromMars;
{
double lat = 0.0;
double lng = 0.0;
transform_mars_2_bear_paw(self.coordinate.latitude, self.coordinate.longitude, &lat, &lng);
return [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(lat, lng)
                                     altitude:self.altitude
                           horizontalAccuracy:self.horizontalAccuracy
                             verticalAccuracy:self.verticalAccuracy
                                       course:self.course
                                        speed:self.speed
                                    timestamp:self.timestamp];
}

- (CLLocation*)locationMarsFromBearPaw;
{
double lat = 0.0;
double lng = 0.0;
transform_bear_paw_2_mars(self.coordinate.latitude, self.coordinate.longitude, &lat, &lng);
return [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(lat, lng)
                                     altitude:self.altitude
                           horizontalAccuracy:self.horizontalAccuracy
                             verticalAccuracy:self.verticalAccuracy
                                       course:self.course
                                        speed:self.speed
                                    timestamp:self.timestamp];
}

@end
// --- transform_earth_2_mars ---

参考来源

使用的时候如下面就可以了

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
CLLocation *newLocation = locations.lastObject;
newLocation = [newLocation locationMarsFromEarth];//将位置转换成中国地图
}

整个demo基本上就这些可以说的点,最后需要赘述一下,如果你的项目里面需要使用这样的功能,在上传App Store的时候,你需要说明清楚你在哪个地方使用后台定位、后台语音播报功能,为什么要使用,之后还需要你录制相关应用的视频(放在优酷网就可以了)。因为比较消耗系统性能,尤其电量,所以审核还是比较严格的,就算被打回来了,根据他的反馈针对性解释清楚,最后还是可以上线的。
最后,如果文章有什么说的不对的地方还请大家不吝赐教,相互学习,谢谢🙏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章