美文网首页键盘上的鼓手iOS菜鸟到大神iOS学习笔记
仿美团外卖可拖拽地图定位--百度地图

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

作者: 张叔叔 | 来源:发表于2016-10-27 14:19 被阅读3438次

前言

这里先跟大家道个歉,之前上传的demo有一个bug,非常感谢@MinJing_Lin 朋友帮我提出来,但是我由于工作原因一直没有修改,今天终于抽空修复了一下,并且更新了demo,首先说一下bug,也可以看评论,就是当在列表中选择一条地址数据的时候,确认返回,界面上显示的并不是你选择的那条数据,bug产生原因:是因为我做了一个处理,当选择一条数据的时候我也同时更新了地图信息,更新地图信息后下面的列表数据也更新了,这个时候选择的数据就已经发生了变化,导致bug的产生,根据大家的业务需求有
以下解决方案:
1.当选择列表数据的时候不更新地图,直接返回界面(不需要确定按钮)
2.当选择列表数据的时候更新地图信息,确认返回的时候,数据返回列表第一条的数据信息,地图更新后会把选择的数据显示在第一条,即使你需要的数据。
3.@MinJing_Lin 评论中说的那个方法也是可以的,把model改成局部变量。
我这里demo中是用第二种方法修复的,大家根据需求自行修改。

在开发项目过程中有时候需要获取用户的精确位置信息,但是我们都知道,直接获取的地址可能会有误差的,这时候就需要手动拖拽地图,然后获取一个精确的位置信息,就像美团外卖的收获地址一样。。。

先上一个效果图看一下

baiduDragMap.gif

界面做的有点粗糙,不过基本想要的数据都能拿到!
下面我就开始教大家具体怎么实现:

第一步

(1)去百度开发者中心添加你的应用,申请appkey。
(2)按照百度地图的开发文档集成SDK到自己的项目中去,这里我用的是pod导入的,比较方便,也便于后期进行更新。
(3)在您的AppDelegate.m文件中添加对BMKMapManager的初始化,并填入您申请的授权Key,示例如下:

_mapManager = [[BMKMapManager alloc]init];
    // 如果要关注网络及授权验证事件,请设定     generalDelegate参数
    BOOL ret = [_mapManager start:@"这里是你申请的appkey"  generalDelegate:self];
    if (!ret) {
        NSLog(@"manager start failed!"); 
    }

第二步

在ZGYDrapMapVC.m文件中初始化BMKMapView地图控件 和BMKGeoCodeSearch geo搜索服务

_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;  //比例尺级别

自2.0.0起,BMKMapView新增viewWillAppear、viewWillDisappear方法来控制BMKMapView的生命周期,并且在一个时刻只能有一个BMKMapView接受回调消息,因此在使用BMKMapView的viewController中需要在viewWillAppear、viewWillDisappear方法中调用BMKMapView的对应的方法,并处理delegate,代码如下:

/**
 管理百度地图的生命周期
 */
- (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,否则影响内存的释放
}

第三步

判断定位是否可用,如果可用就初始化定位服务,并且开启定位服务进行定位服务,如果不可用就提示用户开启定位

/**
 判断定位是否可用并且初始化定位信息
 */
- (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];
}

第四步

开始定位后会在回掉方法中获得一个经纬度,获取到经纬度之后发起一个反地理编码:

/**
 *用户位置更新后,会调用此函数
 *@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];
}

反地理编码搜索发起后会在其代理方法中获得反地理编码的结果

/**
 *返回反地理编码搜索结果
 *@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 {
        // 各种情况的判断。。。
    }
}

这个方法中的result里面包含着反地理编码的name、
所在城市city 、address、经纬度、电话号码phone、邮箱postcode等

我把我需要的数据提取出来转成model放进数组里面,便于在tableView上展示,展示信息如上图所示。

第五步

地图初始化完成后不禁用的情况下是可以拖动的,当地图区域改变后会有代理方法如下:

/**
 *地图区域改变完成后会调用此接口
 *@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检索发送成功");
    }
}
方法中我做了UIview动画,看起来效果会更好一点,这里地图正中间的定位图片并不是地图中的元素,只是一个imageView在屏幕最上方的中间位置,当地图改变的时候让所需要的点也显示在地图中间位置就行了,用到的方法是
/**
 *设定地图中心点坐标
 *@param coordinate 要设定的地图中心点坐标,用经纬度表示
 *@param animated 是否采用动画效果
 */
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;

只要这一点想明白,并且知道百度地图API提供的有这些相关的方法这个问题就非常的容易了。

我这里也用到了自定义打头针的方法

- (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;
}

大家要记得,大头针是跟tableView的cell一样具有重用机制的,处理不好界面会有很多消失不去的大头针,相关问题请参考官方文档,比较详细。

大致的流程就是这些,还需要自己多想多做,问题就没有那么的复杂了!

如果有什么问题希望大家提出来多多讨论,如文中有错误的地方也希望指出,我会及时改正,谢谢大家!

本文Demo已经上传到GitHub ,方便大家去下载使用,代码水平不高,望见谅,如有意见请提出,我会改正!谢谢大家的支持!非常感谢!

相关文章

  • 百度地图检索失败的问题

    由于项目需求,需要使用百度地图做成可拖拽达到精准定位的功能,类似于美团外卖的地图定位。 根据百度地图的API很容易...

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

    前言 在开发项目过程中有时候需要获取用户的精确位置信息,但是我们都知道,直接获取的地址可能会有误差的,这时候就需要...

  • 实现地图选择地址,附近poi检索等....

    布局文件: 附录: 高仿百度外卖地址添加功能(百度地图拖动定位,poi搜索,设置配送范围 百度地图SDK

  • 高德地图仿美团外卖可拖拽地图精准地位

    前言 之前公司老板看上人家美团的这个效果,要在我们公司项目里选择地址的时候也用这种方法就研究了下,但是人家这个是用...

  • 百度地图web--拖拽选址

    实现地图拖拽选址功能,百度地图并未像高德地图拖拽选址功能 。由于项目需要,在百度地图的基础上实现简单的拖拽功能。大...

  • iOS | 地图定位

    在IOS开发中,最常见的功能之一就是地图定位功能,不单单是百度地图,高德地图等专业的地图导航软件,还有美团,咕咚等...

  • iOS(使用百度地图)-仿美团地图功能

    公司项目需要有地图定位的功能,经同事推荐准备仿照美团地图的样式来做。 功能实现思路:通过百度定位获取用户当前坐标,...

  • 2018-09-21

    #两个关于沟通的小案例 ###一 案例1 善于沟通的美团外卖员 ​ 由于外卖送货地址在地图上的定位出现偏差,导致...

  • H5-12.19拖拽事件及百度地图API

    一、 Geolocation(定位) 二、 百度地图 引用百度地图api 密钥获取:首先进入百度地图 点击进入地图...

  • 8.25兄弟会

    js调用百度地图api实现定位 百度地图的API,接口很丰富,实现定位功能 // 百度地图API功能 varmap...

网友评论

  • Ming_06c0:我在这个基础上加了BMKPointAnnotation,然后一直不挺的出发反向搜索。请问有没有方法区分当前地图区域更改时用户拖动触发的方法?
  • 小葡萄_33a2:亲,有一个问题,就是从服务器获得一个经纬度,用百度地图显示出来,可以实现吗,我搜了一下,好像不支持,亲我也需要这个Demo,(1768127233@qq.com)谢谢啦
    张叔叔:@小葡萄_33a2 文章里面有下载链接的 从服务器获取的经纬度可以直接显示出来的 初始化地图之后根据获取的经纬度update一下就好了
  • 沐浴汐:亲 跑你demo到项目 怎么一直是检索失败 这个errorBMK_SEARCH_PERMISSION_UNFINISHED,///还未完成鉴权
  • 别人都说昵称起的长一点比较好:给个demo呗,GitHub上下载的好像没法用
  • unhangcorn:感谢,好人一生平安
  • 346b1179f4f4:能分享一下这个demo吗!邮箱是915938076@qq.com
  • 346b1179f4f4:能给一下这个demo吗!915938076@qq.com
  • 十一岁的加重:if (0 == index) {
    if (![CLLocationManager locationServicesEnabled]) {
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=LOCATION_SERVICES"]];
    }else{
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }
    }

    都是跳啊
    张叔叔:@十一岁的加重 对啊 这个是看你系统定位服务关闭了还是应用的定位服务关闭 不同的情况跳不同的设置界面 只在iOS10之前可用
  • H5:请问拖拽地图 内存一直增加怎么解决
    张叔叔:@H5 不需要的 地图资源本身就需要一定的内存 不使用地图的时候注意delegate = nil 使用block的时候注意循环引用就好了
    H5:@张叔叔 不需要特殊处理
    张叔叔:@H5拖拽地图时候内存是会有所上升 这个内存是加载地图资源的内存 经测试 并不会一直增加 有增有减 退回界面 内存会降下去的
  • minjing_lin:楼主,有个小问题,block 传值失败,从表界面传到地图界面是正常的,但传到第一界面就不是选中的Model数据了,我想到的办法就是下面的方法改下,望指教
    for (int i = 0; i < result.poiList.count; i++) {
    BMKPoiInfo* poi = [result.poiList objectAtIndex:i];
    PoiModel * model = [[PoiModel alloc]init];
    model.name = poi.name;
    model.city = poi.city;
    model.address = poi.address;
    model.lat = poi.pt.latitude;
    model.lon = poi.pt.longitude;
    [array addObject:model];
    }
    self.model = array[0];
    self.poiResultVC.resultListArray = [NSArray arrayWithArray:array];
    }
    张叔叔:@MinJing_Lin 哦 这个我没太在意 谢谢你的指出 我回去看下我的代码 你现在这样修改能解决问题嘛
    minjing_lin:我的理解是:获取数据源时,你用的是self.model 全局变量,就改变了传过来的model 类,所以我在这里用局部变量代替。
    张叔叔:@MinJing_Lin 这个方法只是获取到数据源 传值失败 是不是你选中的时候根据索引值从数据源里面取数据有问题?你断点看一下你取的数据对不对
  • Double丶K:赞一个
    张叔叔:@KK先森 谢谢:pray:
  • Chris杨星:15110369302@163.com,谢谢楼主
    张叔叔:@Chris杨星 评论里面有链接地址 自己下载一下吧 谢谢
  • 不用什么昵称了:求Demo 谢谢楼主,已经喜欢.
    251985069@qq.com
    张叔叔:@不用什么昵称了 demo已经上传了 你在评论里找下链接 谢谢:pray:
  • 小小同: @张叔叔 1730264980@qq.com谢谢。
    张叔叔:@小小同 已经上传GitHub https://github.com/CodingInMan/MeiTuanMapDemo.git
  • me_bingo:893200116@qq.com 谢谢
    张叔叔:@me_bingo 已经上传GitHub https://github.com/CodingInMan/MeiTuanMapDemo.git
  • 流星大石头:demo上传到github吧,供大家下载观摩
    张叔叔:@流星大石头 已经上传!谢谢支持!欢迎提意见!谢谢!https://github.com/CodingInMan/MeiTuanMapDemo.git
  • minjing_lin:楼主,厉害
  • 峰子1994:有demo吗
    张叔叔:@峰子1994 已发
    峰子1994:@张叔叔 1299346106@qq.co m
    张叔叔:@峰子1994 邮箱
  • 小楼东风:我正好也需要这个Demo,820811266@qq.com,谢谢!
    小楼东风:@张叔叔 3Q
    张叔叔:@小楼东风 已发送 查收一下
  • 刺骨寒:demo 给一个白 正好要做百度 地图这个。
    张叔叔:@刺骨寒 可以的 明天我整理一下发给你 留个邮箱 现在电脑没在旁边:stuck_out_tongue_closed_eyes:

本文标题:仿美团外卖可拖拽地图定位--百度地图

本文链接:https://www.haomeiwen.com/subject/rhwfuttx.html