仿美團外賣可拖拽地圖定位--百度地圖

前言

這里先跟大家道個歉,之前上傳的demo有一個bug,非常感謝@MinJing_Lin 朋友幫我提出來,但是我由于工作原因一直沒有修改,今天終于抽空修復了一下,并且更新了demo,首先說一下bug,也可以看評論,就是當在列表中選擇一條地址數(shù)據(jù)的時候,確認返回,界面上顯示的并不是你選擇的那條數(shù)據(jù),bug產(chǎn)生原因:是因為我做了一個處理,當選擇一條數(shù)據(jù)的時候我也同時更新了地圖信息,更新地圖信息后下面的列表數(shù)據(jù)也更新了,這個時候選擇的數(shù)據(jù)就已經(jīng)發(fā)生了變化,導致bug的產(chǎn)生,根據(jù)大家的業(yè)務需求有
以下解決方案:
1.當選擇列表數(shù)據(jù)的時候不更新地圖,直接返回界面(不需要確定按鈕)
2.當選擇列表數(shù)據(jù)的時候更新地圖信息,確認返回的時候,數(shù)據(jù)返回列表第一條的數(shù)據(jù)信息,地圖更新后會把選擇的數(shù)據(jù)顯示在第一條,即使你需要的數(shù)據(jù)。
3.@MinJing_Lin 評論中說的那個方法也是可以的,把model改成局部變量。
我這里demo中是用第二種方法修復的,大家根據(jù)需求自行修改。

在開發(fā)項目過程中有時候需要獲取用戶的精確位置信息,但是我們都知道,直接獲取的地址可能會有誤差的,這時候就需要手動拖拽地圖,然后獲取一個精確的位置信息,就像美團外賣的收獲地址一樣。。。

先上一個效果圖看一下

baiduDragMap.gif

界面做的有點粗糙,不過基本想要的數(shù)據(jù)都能拿到!
下面我就開始教大家具體怎么實現(xiàn):

第一步

(1)去百度開發(fā)者中心添加你的應用,申請appkey。
(2)按照百度地圖的開發(fā)文檔集成SDK到自己的項目中去,這里我用的是pod導入的,比較方便,也便于后期進行更新。
(3)在您的AppDelegate.m文件中添加對BMKMapManager的初始化,并填入您申請的授權Key,示例如下:

_mapManager = [[BMKMapManager alloc]init];
    // 如果要關注網(wǎng)絡及授權驗證事件,請設定     generalDelegate參數(shù)
    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;//設置定位的狀態(tài)  這里需要設置為跟隨狀態(tài),因為要在地圖上能夠顯示當前的位置
 _mapView.showsUserLocation = YES;//顯示定位圖層
 _mapView.zoomLevel = 19.5;  //比例尺級別

自2.0.0起,BMKMapView新增viewWillAppear、viewWillDisappear方法來控制BMKMapView的生命周期,并且在一個時刻只能有一個BMKMapView接受回調(diào)消息,因此在使用BMKMapView的viewController中需要在viewWillAppear、viewWillDisappear方法中調(diào)用BMKMapView的對應的方法,并處理delegate,代碼如下:

/**
 管理百度地圖的生命周期
 */
- (void)viewWillAppear:(BOOL)animated{
    [_mapView viewWillAppear];
    _mapView.delegate = self;
    _geocodesearch.delegate = self;
}

- (void)viewWillDisappear:(BOOL)animated{
    [_mapView viewWillDisappear];
    _mapView.delegate = nil;   //不用的時候要置為nil,否則影響內(nèi)存的釋放
    _geocodesearch.delegate = nil;  //不用的時候要置為nil,否則影響內(nèi)存的釋放
}

第三步

判斷定位是否可用,如果可用就初始化定位服務,并且開啟定位服務進行定位服務,如果不可用就提示用戶開啟定位

/**
 判斷定位是否可用并且初始化定位信息
 */
- (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:@"請確認打開了定位服務,且允許商戶管理系統(tǒng)獲取位置" 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 設置定位參數(shù)
- (void)setLocation
{
    //初始化BMKLocationService
    _locService = [[BMKLocationService alloc]init];
    _locService.delegate = self;
    //設置定位精度
    _locService.desiredAccuracy = kCLLocationAccuracyBest;
    CLLocationDistance distance = 10.0;
    _locService.distanceFilter = distance;
}
- (void)startLocation{
    [_locService startUserLocationService];
}

第四步

開始定位后會在回掉方法中獲得一個經(jīng)緯度,獲取到經(jīng)緯度之后發(fā)起一個反地理編碼:

/**
 *用戶位置更新后,會調(diào)用此函數(shù)
 *@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檢索發(fā)送成功");
    }
    else
    {
        NSLog(@"反geo檢索發(fā)送成功");
    }
    [_mapView updateLocationData:userLocation];
    [_mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
    [_locService stopUserLocationService];
}

/**
 定位失敗

 @param error 定位失敗后的錯誤信息   根據(jù)錯誤信息判斷失敗的原因
 */
- (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];
}

反地理編碼搜索發(fā)起后會在其代理方法中獲得反地理編碼的結果

/**
 *返回反地理編碼搜索結果
 *@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、經(jīng)緯度、電話號碼phone、郵箱postcode等

我把我需要的數(shù)據(jù)提取出來轉(zhuǎn)成model放進數(shù)組里面,便于在tableView上展示,展示信息如上圖所示。

第五步

地圖初始化完成后不禁用的情況下是可以拖動的,當?shù)貓D區(qū)域改變后會有代理方法如下:

/**
 *地圖區(qū)域改變完成后會調(diào)用此接口
 *@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檢索發(fā)送成功");
    }
    else
    {
        NSLog(@"反geo檢索發(fā)送成功");
    }
}
方法中我做了UIview動畫,看起來效果會更好一點,這里地圖正中間的定位圖片并不是地圖中的元素,只是一個imageView在屏幕最上方的中間位置,當?shù)貓D改變的時候讓所需要的點也顯示在地圖中間位置就行了,用到的方法是
/**
 *設定地圖中心點坐標
 *@param coordinate 要設定的地圖中心點坐標,用經(jīng)緯度表示
 *@param animated 是否采用動畫效果
 */
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;

只要這一點想明白,并且知道百度地圖API提供的有這些相關的方法這個問題就非常的容易了。

我這里也用到了自定義打頭針的方法

- (BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation{
    
    static NSString *pinID = @"pinID";
    // 從緩存池取出大頭針數(shù)據(jù)視圖
    BMKAnnotationView *customView = [mapView dequeueReusableAnnotationViewWithIdentifier:pinID];
    // 如果取出的為nil , 那么就手動創(chuàng)建大頭針視圖
    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已經(jīng)上傳到GitHub ,方便大家去下載使用,代碼水平不高,望見諒,如有意見請?zhí)岢?,我會改正!謝謝大家的支持!非常感謝!

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容