仿美团外卖可拖拽地图定位--百度地图

gvpg7853 7年前
   <h2>前言</h2>    <pre>  <code class="language-objectivec">这里先跟大家道个歉,之前上传的demo有一个bug,非常感谢@MinJing_Lin 朋友帮我提出来,但是我由于工作原因一直没有修改,今天终于抽空修复了一下,并且更新了demo,首先说一下bug,也可以看评论,就是当在列表中选择一条地址数据的时候,确认返回,界面上显示的并不是你选择的那条数据,bug产生原因:是因为我做了一个处理,当选择一条数据的时候我也同时更新了地图信息,更新地图信息后下面的列表数据也更新了,这个时候选择的数据就已经发生了变化,导致bug的产生,根据大家的业务需求有  以下解决方案:  1.当选择列表数据的时候不更新地图,直接返回界面(不需要确定按钮)  2.当选择列表数据的时候更新地图信息,确认返回的时候,数据返回列表第一条的数据信息,地图更新后会把选择的数据显示在第一条,即使你需要的数据。  3.@MinJing_Lin 评论中说的那个方法也是可以的,把model改成局部变量。  我这里demo中是用第二种方法修复的,大家根据需求自行修改。</code></pre>    <p>在开发项目过程中有时候需要获取用户的精确位置信息,但是我们都知道,直接获取的地址可能会有误差的,这时候就需要手动拖拽地图,然后获取一个精确的位置信息,就像美团外卖的收获地址一样。。。</p>    <p>先上一个效果图看一下</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c202eab40ae53b6729b7248d1b524f64.gif"></p>    <p>baiduDragMap.gif</p>    <p>界面做的有点粗糙,不过基本想要的数据都能拿到!</p>    <p>下面我就开始教大家具体怎么实现:</p>    <h2>第一步</h2>    <p>(1)去百度开发者中心添加你的应用,申请appkey。</p>    <p>(2)按照百度地图的开发文档集成SDK到自己的项目中去,这里我用的是pod导入的,比较方便,也便于后期进行更新。</p>    <p>(3)在您的AppDelegate.m文件中添加对BMKMapManager的初始化,并填入您申请的授权Key,示例如下:</p>    <pre>  <code class="language-objectivec">_mapManager = [[BMKMapManager alloc]init];      // 如果要关注网络及授权验证事件,请设定     generalDelegate参数      BOOL ret = [_mapManager start:@"这里是你申请的appkey"  generalDelegate:self];      if (!ret) {          NSLog(@"manager start failed!");       }</code></pre>    <h2>第二步</h2>    <p>在ZGYDrapMapVC.m文件中初始化BMKMapView地图控件 和BMKGeoCodeSearch geo搜索服务</p>    <pre>  <code class="language-objectivec">_geocodesearch = [[BMKGeoCodeSearch alloc]init];         //初始化mapView  self.mapView = [[BMKMapView alloc]initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, 300)];   _mapView.userTrackingMode = BMKUserTrackingModeFollow;//设置定位的状态  这里需要设置为跟随状态,因为要在地图上能够显示当前的位置   _mapView.showsUserLocation = YES;//显示定位图层   _mapView.zoomLevel = 19.5;  //比例尺级别</code></pre>    <p>自2.0.0起,BMKMapView新增viewWillAppear、viewWillDisappear方法来控制BMKMapView的生命周期,并且在一个时刻只能有一个BMKMapView接受回调消息,因此在使用BMKMapView的viewController中需要在viewWillAppear、viewWillDisappear方法中调用BMKMapView的对应的方法,并处理delegate,代码如下:</p>    <pre>  <code class="language-objectivec">/**   管理百度地图的生命周期   */  - (void)viewWillAppear:(BOOL)animated{      [_mapView viewWillAppear];      _mapView.delegate = self;      _geocodesearch.delegate = self;  }    - (void)viewWillDisappear:(BOOL)animated{      [_mapView viewWillDisappear];      _mapView.delegate = nil;   //不用的时候要置为nil,否则影响内存的释放      _geocodesearch.delegate = nil;  //不用的时候要置为nil,否则影响内存的释放  }</code></pre>    <h2>第三步</h2>    <p>判断定位是否可用,如果可用就初始化定位服务,并且开启定位服务进行定位服务,如果不可用就提示用户开启定位</p>    <pre>  <code class="language-objectivec">/**   判断定位是否可用并且初始化定位信息   */  - (void)initLocation{      if ([CLLocationManager locationServicesEnabled] &&          ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized           || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)) {              //定位功能可用,开始定位              [self setLocation];              [self startLocation];            }else if (([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) || ([CLLocationManager authorizationStatus]==kCLAuthorizationStatusRestricted)){              [[ZGYAlertView alloc]showAlertViewMessage:nil Title:@"请确认打开了定位服务,且允许商户管理系统获取位置" cancleItem:@"去设置" andOtherItem:nil viewController:self onBlock:^(AlertViewBtnIndex index) {                  if (0 == index) {                      if (![CLLocationManager locationServicesEnabled]) {                          [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=LOCATION_SERVICES"]];                      }else{                          [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];                      }                  }              }];            }  }    #pragma mark 设置定位参数  - (void)setLocation  {      //初始化BMKLocationService      _locService = [[BMKLocationService alloc]init];      _locService.delegate = self;      //设置定位精度      _locService.desiredAccuracy = kCLLocationAccuracyBest;      CLLocationDistance distance = 10.0;      _locService.distanceFilter = distance;  }  - (void)startLocation{      [_locService startUserLocationService];  }</code></pre>    <h2>第四步</h2>    <p>开始定位后会在回掉方法中获得一个经纬度,获取到经纬度之后发起一个反地理编码:</p>    <pre>  <code class="language-objectivec">/**   *用户位置更新后,会调用此函数   *@param userLocation 新的用户位置   */  - (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation  {      [self.view addSubview:self.mapView];      PoiResultListViewController *poiResultVC = (PoiResultListViewController *)self.childViewControllers[0];      self.poiResultVC = poiResultVC;      [self.contentView addSubview:poiResultVC.view];          [UIView animateWithDuration:0.3 delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{          _poiView.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, 150 - 35);      } completion:^(BOOL finished) {          [UIView animateWithDuration:0.3 delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{              _poiView.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, 150 - 20);          } completion:nil];        }];      CLLocationCoordinate2D pt = (CLLocationCoordinate2D){0, 0};      pt = (CLLocationCoordinate2D){userLocation.location.coordinate.latitude, userLocation.location.coordinate.longitude};      BMKReverseGeoCodeOption *reverseGeocodeSearchOption = [[BMKReverseGeoCodeOption alloc]init];      reverseGeocodeSearchOption.reverseGeoPoint = pt;      BOOL flag = [_geocodesearch reverseGeoCode:reverseGeocodeSearchOption];      if(flag)      {          NSLog(@"反geo检索发送成功");      }      else      {          NSLog(@"反geo检索发送成功");      }      [_mapView updateLocationData:userLocation];      [_mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];      [_locService stopUserLocationService];  }    /**   定位失败     @param error 定位失败后的错误信息   根据错误信息判断失败的原因   */  - (void)didFailToLocateUserWithError:(NSError *)error{      //无法获取位置信息  /*这里的alertView是自己封装的alertView便于使用*/      [[ZGYAlertView alloc]showAlertViewMessage:[NSString stringWithFormat:@"错误代码:%ld",[error code]] Title:@"无法获取位置信息" cancleItem:@"取消" andOtherItem:nil viewController:self onBlock:^(AlertViewBtnIndex index) {      }];      [_locService stopUserLocationService];  }</code></pre>    <p>反地理编码搜索发起后会在其代理方法中获得反地理编码的结果</p>    <pre>  <code class="language-objectivec">/**   *返回反地理编码搜索结果   *@param searcher 搜索对象   *@param result 搜索结果   *@param error 错误号,@see BMKSearchErrorCode   */  - (void)onGetReverseGeoCodeResult:(BMKGeoCodeSearch *)searcher result:(BMKReverseGeoCodeResult *)result errorCode:(BMKSearchErrorCode)error{      if (error == BMK_SEARCH_NO_ERROR) {          NSMutableArray *array = [NSMutableArray arrayWithCapacity:0];          for (int i = 0; i < result.poiList.count; i++) {              BMKPoiInfo* poi = [result.poiList objectAtIndex:i];              self.model = [[PoiModel alloc]init];              self.model.name = poi.name;              self.model.city = poi.city;              self.model.address = poi.address;              self.model.lat = poi.pt.latitude;              self.model.lon = poi.pt.longitude;              [array addObject:self.model];          }          self.poiResultVC.resultListArray = [NSArray arrayWithArray:array];        } else if (error == BMK_SEARCH_AMBIGUOUS_ROURE_ADDR){          NSLog(@"起始点有歧义");      } else {          // 各种情况的判断。。。      }  }</code></pre>    <p>这个方法中的result里面包含着反地理编码的name、</p>    <p>所在城市city 、address、经纬度、电话号码phone、邮箱postcode等</p>    <p>我把我需要的数据提取出来转成model放进数组里面,便于在tableView上展示,展示信息如上图所示。</p>    <h2>第五步</h2>    <p>地图初始化完成后不禁用的情况下是可以拖动的,当地图区域改变后会有代理方法如下:</p>    <pre>  <code class="language-objectivec">/**   *地图区域改变完成后会调用此接口   *@param mapview 地图View   *@param animated 是否动画   */  - (void)mapView:(BMKMapView *)mapView regionDidChangeAnimated:(BOOL)animated  {      NSLog(@"%lf -------  %lf",mapView.centerCoordinate.latitude,mapView.centerCoordinate.longitude);      [UIView animateWithDuration:0.3 delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{          _poiView.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, 150 - 35);      } completion:^(BOOL finished) {          [UIView animateWithDuration:0.3 delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{              _poiView.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, 150 - 20);          } completion:nil];        }];      CLLocationCoordinate2D pt = (CLLocationCoordinate2D){0, 0};      pt = (CLLocationCoordinate2D){mapView.centerCoordinate.latitude, mapView.centerCoordinate.longitude};      BMKReverseGeoCodeOption *reverseGeocodeSearchOption = [[BMKReverseGeoCodeOption alloc]init];      reverseGeocodeSearchOption.reverseGeoPoint = pt;      BOOL flag = [_geocodesearch reverseGeoCode:reverseGeocodeSearchOption];      if(flag)      {          NSLog(@"反geo检索发送成功");      }      else      {          NSLog(@"反geo检索发送成功");      }  }</code></pre>    <p>方法中我做了UIview动画,看起来效果会更好一点,这里地图正中间的定位图片并不是地图中的元素,只是一个imageView在屏幕最上方的中间位置,当地图改变的时候让所需要的点也显示在地图中间位置就行了,用到的方法是</p>    <pre>  <code class="language-objectivec">/**   *设定地图中心点坐标   *@param coordinate 要设定的地图中心点坐标,用经纬度表示   *@param animated 是否采用动画效果   */  - (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;</code></pre>    <p>只要这一点想明白,并且知道百度地图API提供的有这些相关的方法这个问题就非常的容易了。</p>    <p>我这里也用到了自定义打头针的方法</p>    <pre>  <code class="language-objectivec">- (BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation{        static NSString *pinID = @"pinID";      // 从缓存池取出大头针数据视图      BMKAnnotationView *customView = [mapView dequeueReusableAnnotationViewWithIdentifier:pinID];      // 如果取出的为nil , 那么就手动创建大头针视图      if (customView == nil) {          customView = [[BMKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinID];      }      // 1. 设置大头针图片      customView.image = [UIImage imageNamed:@"point"];      // 2. 设置弹框      customView.canShowCallout = YES;        return customView;  }</code></pre>    <p>大家要记得,大头针是跟tableView的cell一样具有重用机制的,处理不好界面会有很多消失不去的大头针,相关问题请参考官方文档,比较详细。</p>    <p>大致的流程就是这些,还需要自己多想多做,问题就没有那么的复杂了!</p>    <p>如果有什么问题希望大家提出来多多讨论,如文中有错误的地方也希望指出,我会及时改正,谢谢大家!</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/8ae9a29c9a8e</p>    <p> </p>