天天看點

IOS 地圖定位 概覽 定位 地圖

iOS開發系列--地圖與定位

2014-11-27 08:52 by KenshinCui, 55824 閱讀, 18 評論, 收藏, 編輯

概覽

現在很多社交、電商、團購應用都引入了地圖和定位功能,似乎地圖功能不再是地圖應用和導航應用所特有的。的确,有了地圖和定位功能确實讓我們的生活更加豐富多彩,極大的改變了我們的生活方式。例如你到了一個陌生的地方想要查找附近的酒店、超市等就可以打開軟體搜尋周邊;類似的,還有很多團購軟體可以根據你所在的位置自動為你推薦某些商品。總之,目前地圖和定位功能已經大量引入到應用開發中。今天就和大家一起看一下iOS如何進行地圖和定位開發。

  1. 定位
  2. 地圖

定位

要實作地圖、導航功能,往往需要先熟悉定位功能,在iOS中通過Core Location架構進行定位操作。Core Location自身可以單獨使用,和地圖開發架構MapKit完全是獨立的,但是往往地圖開發要配合定位架構使用。在Core Location中主要包含了定位、地理編碼(包括反編碼)功能。

定位功能

定位是一個很常用的功能,如一些地圖軟體打開之後如果使用者允許軟體定位的話,那麼打開軟體後就會自動鎖定到目前位置,如果使用者手機移動那麼目前位置也會跟随着變化。要實作這個功能需要使用Core Loaction中CLLocationManager類,首先看一下這個類的一些主要方法和屬性:

類方法 說明
+ (BOOL)locationServicesEnabled; 是否啟用定位服務,通常如果使用者沒有啟用定位服務可以提示使用者打開定位服務
+ (CLAuthorizationStatus)authorizationStatus;

定位服務授權狀态,傳回枚舉類型:

kCLAuthorizationStatusNotDetermined: 使用者尚未做出決定是否啟用定位服務

kCLAuthorizationStatusRestricted: 沒有獲得使用者授權使用定位服務,可能使用者沒有自己禁止通路授權

kCLAuthorizationStatusDenied :使用者已經明确禁止應用使用定位服務或者目前系統定位服務處于關閉狀态

kCLAuthorizationStatusAuthorizedAlways: 應用獲得授權可以一直使用定位服務,即使應用不在使用狀态

kCLAuthorizationStatusAuthorizedWhenInUse: 使用此應用過程中允許通路定位服務

屬性 說明
desiredAccuracy

定位精度,枚舉類型:

kCLLocationAccuracyBest:最精确定位

CLLocationAccuracy kCLLocationAccuracyNearestTenMeters:十米誤差範圍

kCLLocationAccuracyHundredMeters:百米誤差範圍

kCLLocationAccuracyKilometer:千米誤差範圍

kCLLocationAccuracyThreeKilometers:三千米誤差範圍

distanceFilter 位置資訊更新最小距離,隻有移動大于這個距離才更新位置資訊,預設為kCLDistanceFilterNone:不進行距離限制
對象方法 說明
startUpdatingLocation 開始定位追蹤,開始定位後将按照使用者設定的更新頻率執行-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;方法回報定位資訊
stopUpdatingLocation 停止定位追蹤
startUpdatingHeading 開始導航方向追蹤
stopUpdatingHeading 停止導航方向追蹤
startMonitoringForRegion:

開始對某個區域進行定位追蹤,開始對某個區域進行定位後。如果使用者進入或者走出某個區域會調用- (void)locationManager:(CLLocationManager *)manager

    didEnterRegion:(CLRegion *)region和- (void)locationManager:(CLLocationManager *)manager

    didExitRegion:(CLRegion *)region代理方法回報相關資訊

stopMonitoringForRegion: 停止對某個區域進行定位追蹤
requestWhenInUseAuthorization 請求獲得應用使用時的定位服務授權,注意使用此方法前在要在info.plist中配置NSLocationWhenInUseUsageDescription
requestAlwaysAuthorization 請求獲得應用一直使用定位服務授權,注意使用此方法前要在info.plist中配置NSLocationAlwaysUsageDescription
代理方法 說明
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations; 位置發生改變後執行(第一次定位到某個位置之後也會執行)

- (void)locationManager:(CLLocationManager *)manager

       didUpdateHeading:(CLHeading *)newHeading;

導航方向發生變化後執行

  - (void)locationManager:(CLLocationManager *)manager

    didEnterRegion:(CLRegion *)region

進入某個區域之後執行

- (void)locationManager:(CLLocationManager *)manager

    didExitRegion:(CLRegion *)region

走出某個區域之後執行

iOS 8 還提供了更加人性化的定位服務選項。App 的定位服務不再僅僅是關閉或打開,現在,定位服務的啟用提供了三個選項,「永不」「使用應用程式期間」和「始終」。同時,考慮到能耗問題,如果一款 App 要求始終能在背景開啟定位服務,iOS 8 不僅會在首次打開 App 時主動向你詢問,還會在日常使用中彈窗提醒你該 App 一直在背景使用定位服務,并詢問你是否繼續允許。在iOS7及以前的版本,如果在應用程式中使用定位服務隻要在程式中調用startUpdatingLocation方法應用就會詢問使用者是否允許此應用是否允許使用定位服務,同時在提示過程中可以通過在info.plist中配置通過配置Privacy - Location Usage Description告訴使用者使用的目的,同時這個配置是可選的。

但是在iOS8中配置配置項發生了變化,可以通過配置NSLocationAlwaysUsageDescription或者NSLocationWhenInUseUsageDescription來告訴使用者使用定位服務的目的,并且注意這個配置是必須的,如果不進行配置則預設情況下應用無法使用定位服務,打開應用不會給出打開定位服務的提示,除非安裝後自己設定此應用的定位服務。同時,在應用程式中需要根據配置對requestAlwaysAuthorization或locationServicesEnabled方法進行請求。由于本人機器已經更新到最新的iOS8.1下面的内容主要針對iOS8,使用iOS7的朋友需要稍作調整。

//
//  KCMainViewController.m
//  CoreLocation
//
//  Created by Kenshin Cui on 14-03-27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"
#import <CoreLocation/CoreLocation.h>

@interface KCMainViewController ()<CLLocationManagerDelegate>{

    CLLocationManager *_locationManager;
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //定位管理器
    _locationManager=[[CLLocationManager alloc]init];
    
    if (![CLLocationManager locationServicesEnabled]) {
        NSLog(@"定位服務目前可能尚未打開,請設定打開!");
        return;
    }
    
    //如果沒有授權則請求使用者授權
    if ([CLLocationManager authorizationStatus]==kCLAuthorizationStatusNotDetermined){
        [_locationManager requestWhenInUseAuthorization];
    }else if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedWhenInUse){
        //設定代理
        _locationManager.delegate=self;
        //設定定位精度
        _locationManager.desiredAccuracy=kCLLocationAccuracyBest;
        //定位頻率,每隔多少米定位一次
        CLLocationDistance distance=10.0;//十米定位一次
        _locationManager.distanceFilter=distance;
        //啟動跟蹤定位
        [_locationManager startUpdatingLocation];
    }
}

#pragma mark - CoreLocation 代理
#pragma mark 跟蹤定位代理方法,每次位置發生變化即會執行(隻要定位到相應位置)
//可以通過模拟器設定一個虛拟位置,否則在模拟器中無法調用此方法
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
    CLLocation *location=[locations firstObject];//取出第一個位置
    CLLocationCoordinate2D coordinate=location.coordinate;//位置坐标
    NSLog(@"經度:%f,緯度:%f,海拔:%f,航向:%f,行走速度:%f",coordinate.longitude,coordinate.latitude,location.altitude,location.course,location.speed);
    //如果不需要實時定位,使用完即使關閉定位服務
    [_locationManager stopUpdatingLocation];
}

@end      

注意:

1.定位頻率和定位精度并不應當越精确越好,需要視實際情況而定,因為越精确越耗性能,也就越費電。

2.定位成功後會根據設定情況頻繁調用-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations方法,這個方法傳回一組地理位置對象數組,每個元素一個CLLocation代表地理位置資訊(包含經度、緯度、海報、行走速度等資訊),之是以傳回數組是因為有些時候一個位置點可能包含多個位置。

3.使用完定位服務後如果不需要實時監控應該立即關閉定位服務以節省資源。

4.除了提供定位功能,CLLocationManager還可以調用startMonitoringForRegion:方法對指定區域進行監控。

地理編碼

除了提供位置跟蹤功能之外,在定位服務中還包含CLGeocoder類用于處理地理編碼和逆地理編碼(又叫反地理編碼)功能。

地理編碼:根據給定的位置(通常是地名)确定地理坐标(經、緯度)。

逆地理編碼:可以根據地理坐标(經、緯度)确定位置資訊(街道、門牌等)。

CLGeocoder最主要的兩個方法就是- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;和- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;,分别用于地理編碼和逆地理編碼。下面簡單示範一下:

//
//  KCMainViewController.m
//  CoreLocation
//
//  Created by Kenshin Cui on 14-03-27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"
#import <CoreLocation/CoreLocation.h>

@interface KCMainViewController ()<CLLocationManagerDelegate>{

    CLGeocoder *_geocoder;
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _geocoder=[[CLGeocoder alloc]init];
    [self getCoordinateByAddress:@"北京"];
    [self getAddressByLatitude:39.54 longitude:116.28];
}

#pragma mark 根據地名确定地理坐标
-(void)getCoordinateByAddress:(NSString *)address{
    //地理編碼
    [_geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
        //取得第一個地标,地标中存儲了詳細的位址資訊,注意:一個地名可能搜尋出多個位址
        CLPlacemark *placemark=[placemarks firstObject];
        
        CLLocation *location=placemark.location;//位置
        CLRegion *region=placemark.region;//區域
        NSDictionary *addressDic= placemark.addressDictionary;//詳細位址資訊字典,包含以下部分資訊
//        NSString *name=placemark.name;//地名
//        NSString *thoroughfare=placemark.thoroughfare;//街道
//        NSString *subThoroughfare=placemark.subThoroughfare; //街道相關資訊,例如門牌等
//        NSString *locality=placemark.locality; // 城市
//        NSString *subLocality=placemark.subLocality; // 城市相關資訊,例如标志性建築
//        NSString *administrativeArea=placemark.administrativeArea; // 州
//        NSString *subAdministrativeArea=placemark.subAdministrativeArea; //其他行政區域資訊
//        NSString *postalCode=placemark.postalCode; //郵編
//        NSString *ISOcountryCode=placemark.ISOcountryCode; //國家編碼
//        NSString *country=placemark.country; //國家
//        NSString *inlandWater=placemark.inlandWater; //水源、湖泊
//        NSString *ocean=placemark.ocean; // 海洋
//        NSArray *areasOfInterest=placemark.areasOfInterest; //關聯的或利益相關的地标
        NSLog(@"位置:%@,區域:%@,詳細資訊:%@",location,region,addressDic);
    }];
}

#pragma mark 根據坐标取得地名
-(void)getAddressByLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude{
    //反地理編碼
    CLLocation *location=[[CLLocation alloc]initWithLatitude:latitude longitude:longitude];
    [_geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
        CLPlacemark *placemark=[placemarks firstObject];
        NSLog(@"詳細資訊:%@",placemark.addressDictionary);
    }];
}

@end      

地圖

iOS從6.0開始地圖資料不再由谷歌驅動,而是改用自家地圖,當然在國内它的資料是由高德地圖提供的。這樣一來,如果在iOS6.0之前進行地圖開發的話使用方法會有所不同,基于目前的情況其實使用iOS6.0之前版本的系統基本已經寥寥無幾了,所有在接下來的内容中不會再針對iOS5及之前版本的地圖開發進行介紹。

在iOS中進行地圖開發主要有兩種方式,一種是直接利用MapKit架構進行地圖開發,利用這種方式可以對地圖進行精準的控制;另一種方式是直接調用蘋果官方自帶的地圖應用,主要用于一些簡單的地圖應用(例如:進行導航覆寫物填充等),無法進行精确的控制。當然,本節重點内容還是前者,後面的内容也會稍加提示。

用MapKit之前需要簡單了解一下MapKit中地圖展示控件MKMapView的的一些常用屬性和方法,具體如下表:

屬性 說明
userTrackingMode

跟蹤類型,是一個枚舉:

MKUserTrackingModeNone :不進行使用者位置跟蹤;

MKUserTrackingModeFollow :跟蹤使用者位置;

MKUserTrackingModeFollowWithHeading :跟蹤使用者位置并且跟蹤使用者前進方向;

mapType

地圖類型,是一個枚舉:

MKMapTypeStandard :标準地圖,一般情況下使用此地圖即可滿足;

MKMapTypeSatellite :衛星地圖;

MKMapTypeHybrid :混合地圖,加載最慢比較消耗資源;

userLocation 使用者位置,隻讀屬性
annotations 目前地圖中的所有大頭針,隻讀屬性
對象方法 說明
- (void)addAnnotation:(id <MKAnnotation>)annotation; 添加大頭針,對應的有添加大頭針數組
- (void)removeAnnotation:(id <MKAnnotation>)annotation; 删除大頭針,對應的有删除大頭針數組
- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated; 設定地圖顯示區域,用于控制目前螢幕顯示地圖範圍
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated; 設定地圖中心點位置
- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view; 将地理坐标(經緯度)轉化為數學坐标(UIKit坐标)
- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view; 将數學坐标轉換為地理坐标
- (MKAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier; 從緩存池中取出大頭針,類似于UITableView中取出UITableViewCell,為了進行性能優化而設計
- (void)selectAnnotation:(id <MKAnnotation>)annotation animated:(BOOL)animated; 選中指定的大頭針
- (void)deselectAnnotation:(id <MKAnnotation>)annotation animated:(BOOL)animated; 取消選中指定的大頭針
代理方法 說明
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation ; 使用者位置發生改變時觸發(第一次定位到使用者位置也會觸發該方法)
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation ; 顯示區域發生改變後觸發
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView; 地圖加載完成後觸發
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation; 顯示大頭針時觸發,傳回大頭針視圖,通常自定義大頭針可以通過此方法進行
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view 點選選中某個大頭針時觸發
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view 取消選中大頭針時觸發
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay 渲染地圖覆寫物時觸發

使用者位置跟蹤

在很多帶有地圖的應用中預設打開地圖都會顯示使用者目前位置,同時将目前位置标記出來放到螢幕中點友善使用者對周圍情況進行檢視。如果在iOS6或者iOS7中實作這個功能隻需要添加地圖控件、設定使用者跟蹤模式、在-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation代理方法中設定地圖中心區域及顯示範圍。但是在iOS8中用法稍有不同:

1.由于在地圖中進行使用者位置跟蹤需要使用定位功能,而定位功能在iOS8中設計發生了變化,是以必須按照前面定位章節中提到的内容進行配置和請求。

2.iOS8中不需要進行中心點的指定,預設會将目前位置設定中心點并自動設定顯示區域範圍。

了解以上兩點,要進行使用者位置跟蹤其實就相當簡單了,值得一提的是-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation這個代理方法。這個方法隻有在定位(利用前面章節中的定位内容)到目前位置之後就會調用,以後每當使用者位置發生改變就會觸發,調用頻率相當頻繁。

大頭針

在iOS開發中經常會标記某個位置,需要使用地圖示注,也就是大家俗稱的“大頭針”。隻要一個NSObject類實作MKAnnotation協定就可以作為一個大頭針,通常會重寫協定中coordinate(标記位置)、title(标題)、subtitle(子标題)三個屬性,然後在程式中建立大頭針對象并調用addAnnotation:方法添加大頭針即可(之是以iOS沒有定義一個基類實作這個協定供開發者使用,多數原因應該是MKAnnotation是一個模型對象,對于多數應用模型會稍有不同,例如後面的内容中會給大頭針模型對象添加其他屬性)。

KCAnnotation.h

//
//  KCAnnotation.h
//  MapKit
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface KCAnnotation : NSObject<MKAnnotation>

@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

@end      

KCMainViewController.m

//
//  KCMainViewController.m
//  MapKit Annotation
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//  37.785834   -122.406417
//  39.92 116.39

#import "KCMainViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "KCAnnotation.h"

@interface KCMainViewController ()<MKMapViewDelegate>{
    CLLocationManager *_locationManager;
    MKMapView *_mapView;
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initGUI];
}

#pragma mark 添加地圖控件
-(void)initGUI{
    CGRect rect=[UIScreen mainScreen].bounds;
    _mapView=[[MKMapView alloc]initWithFrame:rect];
    [self.view addSubview:_mapView];
    //設定代理
    _mapView.delegate=self;
    
    //請求定位服務
    _locationManager=[[CLLocationManager alloc]init];
    if(![CLLocationManager locationServicesEnabled]||[CLLocationManager authorizationStatus]!=kCLAuthorizationStatusAuthorizedWhenInUse){
        [_locationManager requestWhenInUseAuthorization];
    }
    
    //使用者位置追蹤(使用者位置追蹤用于标記使用者目前位置,此時會調用定位服務)
    _mapView.userTrackingMode=MKUserTrackingModeFollow;
    
    //設定地圖類型
    _mapView.mapType=MKMapTypeStandard;
    
    //添加大頭針
    [self addAnnotation];
}

#pragma mark 添加大頭針 
-(void)addAnnotation{
    CLLocationCoordinate2D location1=CLLocationCoordinate2DMake(39.95, 116.35);
    KCAnnotation *annotation1=[[KCAnnotation alloc]init];
    [email protected]"CMJ Studio";
    [email protected]"Kenshin Cui's Studios";
    annotation1.coordinate=location1;
    [_mapView addAnnotation:annotation1];
    
    CLLocationCoordinate2D location2=CLLocationCoordinate2DMake(39.87, 116.35);
    KCAnnotation *annotation2=[[KCAnnotation alloc]init];
    [email protected]"Kenshin&Kaoru";
    [email protected]"Kenshin Cui's Home";
    annotation2.coordinate=location2;
    [_mapView addAnnotation:annotation2];
}

#pragma mark - 地圖控件代理方法
#pragma mark 更新使用者位置,隻要使用者改變則調用此方法(包括第一次定位到使用者位置)
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
    
    NSLog(@"%@",userLocation);
    //設定地圖顯示範圍(如果不進行區域設定會自動顯示區域範圍并指定目前使用者位置為地圖中心點)
    //    MKCoordinateSpan span=MKCoordinateSpanMake(0.01, 0.01);
    //    MKCoordinateRegion region=MKCoordinateRegionMake(userLocation.location.coordinate, span);
    //    [_mapView setRegion:region animated:true];
}

@end      

運作效果:

IOS 地圖定位 概覽 定位 地圖

設定大頭針視圖

在一些應用中系統預設的大頭針樣式可能無法滿足實際的需求,此時就需要修改大頭針視圖預設樣式。根據前面MapKit的代理方法不難發現- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation;方法可以傳回一個大頭針視圖,隻要實作這個方法并在這個方法中定義一個大頭針視圖MKAnnotationView對象并設定相關屬性就可以改變預設大頭針的樣式。MKAnnotationView常用屬性:

屬性 說明
annotation 大頭針模型資訊,包括标題、子标題、地理位置。
image 大頭針圖檔
canShowCallout 點選大頭針是否顯示标題、子标題内容等,注意如果在- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation;方法中重新定義大頭針預設情況是無法互動的需要設定為true。
calloutOffset 點選大頭針時彈出詳情資訊視圖的偏移量
selected 是否被選中狀态
leftCalloutAccessoryView 彈出詳情左側視圖
rightCalloutAccessoryView 彈出詳情右側視圖

需要注意:

a.這個代理方法的調用時機:每當有大頭針顯示到系統可視界面中時就會調用此方法傳回一個大頭針視圖放到界面中,同時目前系統位置标注(也就是地圖中藍色的位置點)也是一個大頭針,也會調用此方法,是以處理大頭針視圖時需要差別對待。

b.類似于UITableView的代理方法,此方法調用頻繁,開發過程中需要重複利用MapKit的緩存池将大頭針視圖緩存起來重複利用。

c.自定義大頭針預設情況下不允許互動,如果互動需要設定canShowCallout=true

d.如果代理方法傳回nil則會使用預設大頭針視圖,需要根據情況設定。

下面以一個示例進行大頭針視圖設定,這裡設定了大頭針的圖檔、彈出視圖、偏移量等資訊。

KCAnnotation.h

//
//  KCAnnotation.h
//  MapKit
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface KCAnnotation : NSObject<MKAnnotation>

@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

#pragma mark 自定義一個圖檔屬性在建立大頭針視圖時使用
@property (nonatomic,strong) UIImage *image;

@end      

KCMainViewController.m

//
//  KCMainViewController.m
//  MapKit Annotation
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//  37.785834   -122.406417
//  39.92 116.39

#import "KCMainViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "KCAnnotation.h"

@interface KCMainViewController ()<MKMapViewDelegate>{
    CLLocationManager *_locationManager;
    MKMapView *_mapView;
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initGUI];
}

#pragma mark 添加地圖控件
-(void)initGUI{
    CGRect rect=[UIScreen mainScreen].bounds;
    _mapView=[[MKMapView alloc]initWithFrame:rect];
    [self.view addSubview:_mapView];
    //設定代理
    _mapView.delegate=self;
    
    //請求定位服務
    _locationManager=[[CLLocationManager alloc]init];
    if(![CLLocationManager locationServicesEnabled]||[CLLocationManager authorizationStatus]!=kCLAuthorizationStatusAuthorizedWhenInUse){
        [_locationManager requestWhenInUseAuthorization];
    }
    
    //使用者位置追蹤(使用者位置追蹤用于标記使用者目前位置,此時會調用定位服務)
    _mapView.userTrackingMode=MKUserTrackingModeFollow;
    
    //設定地圖類型
    _mapView.mapType=MKMapTypeStandard;
    
    //添加大頭針
    [self addAnnotation];
}

#pragma mark 添加大頭針 
-(void)addAnnotation{
    CLLocationCoordinate2D location1=CLLocationCoordinate2DMake(39.95, 116.35);
    KCAnnotation *annotation1=[[KCAnnotation alloc]init];
    [email protected]"CMJ Studio";
    [email protected]"Kenshin Cui's Studios";
    annotation1.coordinate=location1;
    annotation1.image=[UIImage imageNamed:@"icon_pin_floating.png"];
    [_mapView addAnnotation:annotation1];
    
    CLLocationCoordinate2D location2=CLLocationCoordinate2DMake(39.87, 116.35);
    KCAnnotation *annotation2=[[KCAnnotation alloc]init];
    [email protected]"Kenshin&Kaoru";
    [email protected]"Kenshin Cui's Home";
    annotation2.coordinate=location2;
    annotation2.image=[UIImage imageNamed:@"icon_paopao_waterdrop_streetscape.png"];
    [_mapView addAnnotation:annotation2];
}

#pragma mark - 地圖控件代理方法
#pragma mark 顯示大頭針時調用,注意方法中的annotation參數是即将顯示的大頭針對象
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
    //由于目前位置的标注也是一個大頭針,是以此時需要判斷,此代理方法傳回nil使用預設大頭針視圖
    if ([annotation isKindOfClass:[KCAnnotation class]]) {
        static NSString *[email protected]"AnnotationKey1";
        MKAnnotationView *annotationView=[_mapView dequeueReusableAnnotationViewWithIdentifier:key1];
        //如果緩存池中不存在則建立
        if (!annotationView) {
            annotationView=[[MKAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:key1];
            annotationView.canShowCallout=true;//允許互動點選
            annotationView.calloutOffset=CGPointMake(0, 1);//定義詳情視圖偏移量
            annotationView.leftCalloutAccessoryView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_classify_cafe.png"]];//定義詳情左側視圖
        }

        //修改大頭針視圖
        //重新設定此類大頭針視圖的大頭針模型(因為有可能是從緩存池中取出來的,位置是放到緩存池時的位置)
        annotationView.annotation=annotation;
        annotationView.image=((KCAnnotation *)annotation).image;//設定大頭針視圖的圖檔
        
        return annotationView;
    }else {
        return nil;
    }
}
@end      

運作效果:

IOS 地圖定位 概覽 定位 地圖

注意:

在MapKit架構中除了MKAnnotationView之外還有一個MKPinAnnotationView,它是MKAnnotationView的子類,相比MKAnnotationView多了兩個屬性pinColor和animationDrop,分别用于設定大頭針視圖顔色和添加大頭針動畫。

擴充--自定義大頭針彈詳情視圖

通過上面的示例不難看出MKAnnotationView足夠強大(何況還有MKPinAnnotationView),很多資訊都可以進行設定,但是唯獨不能修改大頭針描述詳情視圖(僅僅支援詳情中左右視圖内容)。要實作這個需求目前開發中普遍采用的思路就是:

a.點選一個大頭針A時重新在A的坐标處添加另一個大頭針B(注意此時将A對應的大頭針視圖canShowCallout設定為false)作為大頭針詳情模型,然後在- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation;代理方法中判斷大頭針類型,如果是B則重寫MKAnnotationView(可以自定義一個類C繼承于MKAnnotationView),傳回自定義大頭針視圖C。

b.定義大頭針視圖C繼承于MKAnnotationView(或者MKPinAnnotationView),在自定義大頭針視圖中添加自己的控件,完成自定義布局。

在使用百度地圖用戶端時當點選一個搜尋位置時可以看到此位置的評價等資訊,視圖效果大概如下:

IOS 地圖定位 概覽 定位 地圖

下面不妨試着實作一下這個效果:

大頭針模型:KCAnnotation.h

//
//  KCAnnotation.h
//  MapKit
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface KCAnnotation : NSObject<MKAnnotation>

@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

#pragma mark 自定義一個圖檔屬性在建立大頭針視圖時使用
@property (nonatomic,strong) UIImage *image;

#pragma mark 大頭針詳情左側圖示
@property (nonatomic,strong) UIImage *icon;
#pragma mark 大頭針詳情描述 
@property (nonatomic,copy) NSString *detail;
#pragma mark 大頭針右下方星級評價
@property (nonatomic,strong) UIImage *rate;

@end      

彈出詳情大頭針模型:KCCalloutAnnotation.h

//
//  KCCalloutAnnotation.h
//  MapKit
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface KCCalloutAnnotation : NSObject<MKAnnotation>

@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy,readonly) NSString *title;
@property (nonatomic, copy,readonly) NSString *subtitle;

#pragma mark 左側圖示
@property (nonatomic,strong) UIImage *icon;
#pragma mark 詳情描述
@property (nonatomic,copy) NSString *detail;
#pragma mark 星級評價
@property (nonatomic,strong) UIImage *rate;

@end      

彈出詳情大頭針視圖:KCCalloutAnnotatonView.h

//
//  KCCalloutView.h
//  MapKit
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//  自定義彈出标注視圖

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "KCCalloutAnnotation.h"

@interface KCCalloutAnnotationView : MKAnnotationView

@property (nonatomic ,strong) KCCalloutAnnotation *annotation;

#pragma mark 從緩存取出标注視圖
+(instancetype)calloutViewWithMapView:(MKMapView *)mapView;

@end      

KCCalloutAnnotationView.m

//
//  KCCalloutView.m
//  MapKit
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCCalloutAnnotationView.h"
#define kSpacing 5
#define kDetailFontSize 12
#define kViewOffset 80

@interface KCCalloutAnnotationView(){
    UIView *_backgroundView;
    UIImageView *_iconView;
    UILabel *_detailLabel;
    UIImageView *_rateView;
}

@end

@implementation KCCalloutAnnotationView

-(instancetype)init{
    if(self=[super init]){
        [self layoutUI];
    }
    return self;
}
-(instancetype)initWithFrame:(CGRect)frame{
    if (self=[super initWithFrame:frame]) {
        [self layoutUI];
    }
    return self;
}

-(void)layoutUI{
    //背景
    _backgroundView=[[UIView alloc]init];
    _backgroundView.backgroundColor=[UIColor whiteColor];
    //左側添加圖示
    _iconView=[[UIImageView alloc]init];
    
    //上方詳情
    _detailLabel=[[UILabel alloc]init];
    _detailLabel.lineBreakMode=NSLineBreakByWordWrapping;
    //[_text sizeToFit];
    _detailLabel.font=[UIFont systemFontOfSize:kDetailFontSize];
    
    //下方星級
    _rateView=[[UIImageView alloc]init];
    
    [self addSubview:_backgroundView];
    [self addSubview:_iconView];
    [self addSubview:_detailLabel];
    [self addSubview:_rateView];
}

+(instancetype)calloutViewWithMapView:(MKMapView *)mapView{
    static NSString *[email protected]"calloutKey1";
    KCCalloutAnnotationView *calloutView=(KCCalloutAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:calloutKey];
    if (!calloutView) {
        calloutView=[[KCCalloutAnnotationView alloc]init];
    }
    return calloutView;
}

#pragma mark 當給大頭針視圖設定大頭針模型時可以在此處根據模型設定視圖内容
-(void)setAnnotation:(KCCalloutAnnotation *)annotation{
    [super setAnnotation:annotation];
    //根據模型調整布局
    _iconView.image=annotation.icon;
    _iconView.frame=CGRectMake(kSpacing, kSpacing, annotation.icon.size.width, annotation.icon.size.height);
    
    _detailLabel.text=annotation.detail;
    float detailWidth=150.0;
    CGSize detailSize= [annotation.detail boundingRectWithSize:CGSizeMake(detailWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:kDetailFontSize]} context:nil].size;
    float detailX=CGRectGetMaxX(_iconView.frame)+kSpacing;
    _detailLabel.frame=CGRectMake(detailX, kSpacing, detailSize.width, detailSize.height);
    _rateView.image=annotation.rate;
    _rateView.frame=CGRectMake(detailX, CGRectGetMaxY(_detailLabel.frame)+kSpacing, annotation.rate.size.width, annotation.rate.size.height);
    
    float backgroundWidth=CGRectGetMaxX(_detailLabel.frame)+kSpacing;
    float backgroundHeight=_iconView.frame.size.height+2*kSpacing;
    _backgroundView.frame=CGRectMake(0, 0, backgroundWidth, backgroundHeight);
    self.bounds=CGRectMake(0, 0, backgroundWidth, backgroundHeight+kViewOffset);
    
}
@end      

主視圖控制器:KCMainViewController.m

//
//  KCMainViewController.m
//  MapKit Annotation
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//  37.785834   -122.406417
//  39.92 116.39

#import "KCMainViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "KCAnnotation.h"
#import "KCCalloutAnnotationView.h"
#import "KCCalloutAnnotationView.h"

@interface KCMainViewController ()<MKMapViewDelegate>{
    CLLocationManager *_locationManager;
    MKMapView *_mapView;
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initGUI];
}

#pragma mark 添加地圖控件
-(void)initGUI{
    CGRect rect=[UIScreen mainScreen].bounds;
    _mapView=[[MKMapView alloc]initWithFrame:rect];
    [self.view addSubview:_mapView];
    //設定代理
    _mapView.delegate=self;
    
    //請求定位服務
    _locationManager=[[CLLocationManager alloc]init];
    if(![CLLocationManager locationServicesEnabled]||[CLLocationManager authorizationStatus]!=kCLAuthorizationStatusAuthorizedWhenInUse){
        [_locationManager requestWhenInUseAuthorization];
    }
    
    //使用者位置追蹤(使用者位置追蹤用于标記使用者目前位置,此時會調用定位服務)
    _mapView.userTrackingMode=MKUserTrackingModeFollow;
    
    //設定地圖類型
    _mapView.mapType=MKMapTypeStandard;
    
    //添加大頭針
    [self addAnnotation];
}

#pragma mark 添加大頭針 
-(void)addAnnotation{
    CLLocationCoordinate2D location1=CLLocationCoordinate2DMake(39.95, 116.35);
    KCAnnotation *annotation1=[[KCAnnotation alloc]init];
    [email protected]"CMJ Studio";
    [email protected]"Kenshin Cui's Studios";
    annotation1.coordinate=location1;
    annotation1.image=[UIImage imageNamed:@"icon_pin_floating.png"];
    annotation1.icon=[UIImage imageNamed:@"icon_mark1.png"];
    [email protected]"CMJ Studio...";
    annotation1.rate=[UIImage imageNamed:@"icon_Movie_Star_rating.png"];
    [_mapView addAnnotation:annotation1];
    
    CLLocationCoordinate2D location2=CLLocationCoordinate2DMake(39.87, 116.35);
    KCAnnotation *annotation2=[[KCAnnotation alloc]init];
    [email protected]"Kenshin&Kaoru";
    [email protected]"Kenshin Cui's Home";
    annotation2.coordinate=location2;
    annotation2.image=[UIImage imageNamed:@"icon_paopao_waterdrop_streetscape.png"];
    annotation2.icon=[UIImage imageNamed:@"icon_mark2.png"];
    [email protected]"Kenshin Cui...";
    annotation2.rate=[UIImage imageNamed:@"icon_Movie_Star_rating.png"];
    [_mapView addAnnotation:annotation2];
}

#pragma mark - 地圖控件代理方法
#pragma mark 顯示大頭針時調用,注意方法中的annotation參數是即将顯示的大頭針對象
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
    //由于目前位置的标注也是一個大頭針,是以此時需要判斷,此代理方法傳回nil使用預設大頭針視圖
    if ([annotation isKindOfClass:[KCAnnotation class]]) {
        static NSString *[email protected]"AnnotationKey1";
        MKAnnotationView *annotationView=[_mapView dequeueReusableAnnotationViewWithIdentifier:key1];
        //如果緩存池中不存在則建立
        if (!annotationView) {
            annotationView=[[MKAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:key1];
//            annotationView.canShowCallout=true;//允許互動點選
            annotationView.calloutOffset=CGPointMake(0, 1);//定義詳情視圖偏移量
            annotationView.leftCalloutAccessoryView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_classify_cafe.png"]];//定義詳情左側視圖
        }

        //修改大頭針視圖
        //重新設定此類大頭針視圖的大頭針模型(因為有可能是從緩存池中取出來的,位置是放到緩存池時的位置)
        annotationView.annotation=annotation;
        annotationView.image=((KCAnnotation *)annotation).image;//設定大頭針視圖的圖檔
        
        return annotationView;
    }else if([annotation isKindOfClass:[KCCalloutAnnotation class]]){
        //對于作為彈出詳情視圖的自定義大頭針視圖無彈出互動功能(canShowCallout=false,這是預設值),在其中可以自由添加其他視圖(因為它本身繼承于UIView)
        KCCalloutAnnotationView *calloutView=[KCCalloutAnnotationView calloutViewWithMapView:mapView];
        calloutView.annotation=annotation;
        return calloutView;
    } else {
        return nil;
    }
}

#pragma mark 選中大頭針時觸發
//點選一般的大頭針KCAnnotation時添加一個大頭針作為所點大頭針的彈出詳情視圖
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
    KCAnnotation *annotation=view.annotation;
    if ([view.annotation isKindOfClass:[KCAnnotation class]]) {
        //點選一個大頭針時移除其他彈出詳情視圖
//        [self removeCustomAnnotation];
        //添加詳情大頭針,渲染此大頭針視圖時将此模型對象指派給自定義大頭針視圖完成自動布局
        KCCalloutAnnotation *annotation1=[[KCCalloutAnnotation alloc]init];
        annotation1.icon=annotation.icon;
        annotation1.detail=annotation.detail;
        annotation1.rate=annotation.rate;
        annotation1.coordinate=view.annotation.coordinate;
        [mapView addAnnotation:annotation1];
    }
}

#pragma mark 取消選中時觸發
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{
    [self removeCustomAnnotation];
}

#pragma mark 移除所用自定義大頭針
-(void)removeCustomAnnotation{
    [_mapView.annotations enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj isKindOfClass:[KCCalloutAnnotation class]]) {
            [_mapView removeAnnotation:obj];
        }
    }];
}
@end       

在這個過程中需要注意幾點:

1.大頭針A作為一個普通大頭針,其中最好儲存自定義大頭針視圖C所需要的模型以便根據不同的模型初始化視圖。

2.自定義大頭針視圖C的大頭針模型B中不需要title、subtitle屬性,最好設定為隻讀;模型中最後儲存自定義大頭針視圖C所需要的布局模型資料。

3.隻有點選非B類大頭針時才新增自定義大頭針,并且增加時要首先移除其他B類大頭針避免重疊(一般建議放到取消大頭針選擇的代理方法中)。

4.通常在自定義大頭針視圖C設定大頭針模型時布局界面,此時需要注意新增大頭針的位置,通常需要偏移一定的距離才能達到理想的效果。

運作效果:

IOS 地圖定位 概覽 定位 地圖

使用自帶的地圖應用

除了可以使用MapKit架構進行地圖開發,對地圖有精确的控制和自定義之外,如果對于應用沒有特殊要求的話選用蘋果自帶的地圖應用也是一個不錯的選擇。使用蘋果自帶的應用時需要用到MapKit中的MKMapItem類,這個類有一個openInMapsWithLaunchOptions:動态方法和一個openMapsWithItems: launchOptions:靜态方法用于打開蘋果地圖應用。第一個方法用于在地圖上标注一個位置,第二個方法除了可以标注多個位置外還可以進行多個位置之間的駕駛導航,使用起來也是相當友善。在熟悉這兩個方法使用之前有必要對兩個方法中的options參數做一下簡單說明:

鍵(常量) 說明
MKLaunchOptionsDirectionsModeKey 路線模式,常量

MKLaunchOptionsDirectionsModeDriving  駕車模式

MKLaunchOptionsDirectionsModeWalking 步行模式

MKLaunchOptionsMapTypeKey 地圖類型,枚舉

MKMapTypeStandard :标準模式

MKMapTypeSatellite :衛星模式

MKMapTypeHybrid  :混合模式

MKLaunchOptionsMapCenterKey 中心點坐标,CLLocationCoordinate2D類型
MKLaunchOptionsMapSpanKey 地圖顯示跨度,MKCoordinateSpan 類型
MKLaunchOptionsShowsTrafficKey 是否 顯示交通狀況,布爾型
MKLaunchOptionsCameraKey

3D地圖效果,MKMapCamera類型

注意:此屬性從iOS7及以後可用,前面的屬性從iOS6開始可用

單個位置的标注

下面的代碼示範了如何在蘋果自帶地圖應用上标記一個位置,首先根據反地理編碼獲得一個CLPlacemark位置對象,然後将其轉換為MKPlacemark對象用于MKMapItem初始化,最後調用其openInMapsWithLaunchOptions:打開地圖應用并标記:

//
//  KCMainViewController.m
//  AppleMap
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface KCMainViewController ()
@property (nonatomic,strong) CLGeocoder *geocoder;
@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _geocoder=[[CLGeocoder alloc]init];
    
    [self location];
}

#pragma mark 在地圖上定位
-(void)location{
    //根據“北京市”進行地理編碼
    [_geocoder geocodeAddressString:@"北京市" completionHandler:^(NSArray *placemarks, NSError *error) {
        CLPlacemark *clPlacemark=[placemarks firstObject];//擷取第一個地标
        MKPlacemark *mkplacemark=[[MKPlacemark alloc]initWithPlacemark:clPlacemark];//定位地标轉化為地圖的地标
        NSDictionary *[email protected]{MKLaunchOptionsMapTypeKey:@(MKMapTypeStandard)};
        MKMapItem *mapItem=[[MKMapItem alloc]initWithPlacemark:mkplacemark];
        [mapItem openInMapsWithLaunchOptions:options];
    }];
}
@end      

運作效果:

IOS 地圖定位 概覽 定位 地圖

标記多個位置

如果要标記多個位置需要調用MKMapItem的靜态方法,下面的代碼示範中需要注意,使用CLGeocoder進行定位時一次隻能定位到一個位置,是以第二個位置定位放到了第一個位置擷取成功之後。

//
//  KCMainViewController.m
//  AppleMap
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface KCMainViewController ()
@property (nonatomic,strong) CLGeocoder *geocoder;
@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _geocoder=[[CLGeocoder alloc]init];
    
    [self listPlacemark];
}

-(void)listPlacemark{
    //根據“北京市”進行地理編碼
    [_geocoder geocodeAddressString:@"北京市" completionHandler:^(NSArray *placemarks, NSError *error) {
        CLPlacemark *clPlacemark1=[placemarks firstObject];//擷取第一個地标
        MKPlacemark *mkPlacemark1=[[MKPlacemark alloc]initWithPlacemark:clPlacemark1];
        //注意地理編碼一次隻能定位到一個位置,不能同時定位,所在放到第一個位置定位完成回調函數中再次定位
        [_geocoder geocodeAddressString:@"鄭州市" completionHandler:^(NSArray *placemarks, NSError *error) {
            CLPlacemark *clPlacemark2=[placemarks firstObject];//擷取第一個地标
            MKPlacemark *mkPlacemark2=[[MKPlacemark alloc]initWithPlacemark:clPlacemark2];
            NSDictionary *[email protected]{MKLaunchOptionsMapTypeKey:@(MKMapTypeStandard)};
            //MKMapItem *mapItem1=[MKMapItem mapItemForCurrentLocation];//目前位置
            MKMapItem *mapItem1=[[MKMapItem alloc]initWithPlacemark:mkPlacemark1];
            MKMapItem *mapItem2=[[MKMapItem alloc]initWithPlacemark:mkPlacemark2];
            [MKMapItem openMapsWithItems:@[mapItem1,mapItem2] launchOptions:options];
            
        }];
        
    }];
}
@end      

運作效果:

IOS 地圖定位 概覽 定位 地圖

地圖導航

要使用地圖導航功能在自帶地圖應用中相當簡單,隻要設定參數配置導航模式即可,例如在上面代碼基礎上設定駕駛模式,則地圖應用會啟動駕駛模式計算兩點之間的距離同時對路線進行規劃。

//
//  KCMainViewController.m
//  AppleMap
//
//  Created by Kenshin Cui on 14/3/27.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface KCMainViewController ()
@property (nonatomic,strong) CLGeocoder *geocoder;
@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _geocoder=[[CLGeocoder alloc]init];
    
    [self turnByTurn];
}

-(void)turnByTurn{
    //根據“北京市”地理編碼
    [_geocoder geocodeAddressString:@"北京市" completionHandler:^(NSArray *placemarks, NSError *error) {
        CLPlacemark *clPlacemark1=[placemarks firstObject];//擷取第一個地标
        MKPlacemark *mkPlacemark1=[[MKPlacemark alloc]initWithPlacemark:clPlacemark1];
        //注意地理編碼一次隻能定位到一個位置,不能同時定位,所在放到第一個位置定位完成回調函數中再次定位
        [_geocoder geocodeAddressString:@"鄭州市" completionHandler:^(NSArray *placemarks, NSError *error) {
            CLPlacemark *clPlacemark2=[placemarks firstObject];//擷取第一個地标
            MKPlacemark *mkPlacemark2=[[MKPlacemark alloc]initWithPlacemark:clPlacemark2];
            NSDictionary *[email protected]{MKLaunchOptionsMapTypeKey:@(MKMapTypeStandard),MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving};
            //MKMapItem *mapItem1=[MKMapItem mapItemForCurrentLocation];//目前位置
            MKMapItem *mapItem1=[[MKMapItem alloc]initWithPlacemark:mkPlacemark1];
            MKMapItem *mapItem2=[[MKMapItem alloc]initWithPlacemark:mkPlacemark2];
            [MKMapItem openMapsWithItems:@[mapItem1,mapItem2] launchOptions:options];
            
        }];
        
    }];
}
@end      

運作效果:

IOS 地圖定位 概覽 定位 地圖
注意:其實如果不用蘋果自帶的地圖應用也可以實作地圖導航,MapKit中提供了MKDirectionRequest對象用于計算路線,提供了MKDirections用于計算方向,這樣一來隻需要調用MKMapView的addOverlay等方法添加覆寫物即可實作類似的效果,有興趣的朋友可以試一下。

由于定位和地圖架構中用到了諸多類,有些初學者容易混淆,下面簡單對比一下。

CLLocation:用于表示位置資訊,包含地理坐标、海拔等資訊,包含在CoreLoaction架構中。

MKUserLocation:一個特殊的大頭針,表示使用者目前位置。

CLPlacemark:定位架構中地标類,封裝了詳細的地理資訊。

MKPlacemark:類似于CLPlacemark,隻是它在MapKit架構中,可以根據CLPlacemark建立MKPlacemark。