1、擷取當前定位
iOS提供了一個叫作CoreLocation.framework的架構。使用他可以取到自己的定位資訊(經緯度)。請參考下面程式碼片段:
01if([CLLocationManager locationServicesEnabled]){02 //定位功能開啟的情況下進行定位03 CLLocationManager *manager = [[CLLocationManager alloc] init];04 manager.distanceFilter = kCLDistanceFilterNone;05 manager.desiredAccuracy = kCLLocationAccuracyBest;06 manager.delegate = self;07 [manager startUpdatingLocation];08}09- (void)locationManager:(CLLocationManager *)manager10 didUpdateToLocation:(CLLocation *)newLocation11 fromLocation:(CLLocation *)oldLocation12{13 14 [manager stopUpdatingLocation];15}16- (void)locationManager:(CLLocationManager *)manager17 didFailWithError:(NSError *)error18{19 [manager stopUpdatingLocation];20}
如上面代碼所示CLLocationManager就是用於擷取定位資訊對象類,在實際應用中可以根據自己的需要來設定定位的更新頻率以及定位準確度。其中代碼中的distanceFilter表示更新位置的距離,假如超過設定值則進行定點更新,否則不更新。代碼中的kCLDistanceFilterNone表示不設定距離過濾,即隨時更新地理位置。desiredAccuracy屬性工作表示取得定位的精度,kCLLocationAccuracyBest表示最精確,但也預示著需要消耗更多的時間和電量,所以應該根據需要設定。
那麼CLLocationManager是通過什麼方法來開啟定位的呢?他是通過調用startUpdatingLocation開啟定位功能,然後使用stopUpdatingLocation停止定位,其中定位資訊是通過loctionManager:didUpdateToLocation:fromLocation;委託方法來通知委派物件的,因此委派物件必須實現CLLocationManagerDelegate委託。在返回定位資訊委託方法中主要的兩個參數是newLocation和oldLocation,newLocation表示最新定位,oldLocation表示上一次的定位資訊。這兩個都是CLLocation對象。以下是CLLocation的屬性說明:
屬性
描述
altitude
海拔高度
coordinate
經緯度
course
行駛方向
horizontalAccuracy
水平方向的精確度
Speed
行駛速度
timestamp
時間戳記
verticalAccuracy
垂直方向的精確度
2、擷取地理位置資訊
當你取到了一個經緯度資訊時,也許還有這樣的一個需求,那就是當前的經緯度所對應的地理位置資訊是什麼。那麼這時候我們需要用到架構來為我們實現這一功能,那就是MapKit.framework。在這個架構中有一個叫MKReverseGeocoder的類可以協助我們實現反向解析地理位置。請看一下代碼:
01- (void)locationManager:(CLLocationManager *)manager02 didUpdateToLocation:(CLLocation *)newLocation03 fromLocation:(CLLocation *)oldLocation04{05 MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate]; 06 geocoder.delegate = self; 07 [geocoder start];08}09 10- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder11 didFindPlacemark:(MKPlacemark *)placemark12{13 NSLog(@"\n country:%@\n postalCode:%@\n ISOcountryCode:%@\n locality:%@\n subLocality:%@\n administrativeArea:%@\n subAdministrativeArea:%@\n thoroughfare:%@\n subThoroughfare:%@\n",14 placemark.country, 15 placemark.postalCode, 16 placemark.ISOcountryCode, 17 placemark.administrativeArea, 18 placemark.subAdministrativeArea, 19 placemark.locality, 20 placemark.subLocality, 21 placemark.thoroughfare,22 placemark.subThoroughfare);23} 24 25- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder26 didFailWithError:(NSError *)error27{ 28 NSLog(@"reverse geocoder fail!!");29}
上面的代碼是在擷取到經緯度後,立刻進行反向地理位置解析的。其實MKReverseGeocoder用法也比較簡單,通過經緯度初始化後直接調用start方法就可以實現反向解析了,然後等待返回,其返回是通過委託形式通知的。所以委派物件必須實現MKReverseGeocoderDelegate委託。解析成功後會返回一個MKPlacemark的對象裡麵包含了相關的地理位置資訊(包括國家、地區、街道等)。
但從iOS5之後MKReverseGeocoder成為了不推薦使用的類。因此有一個新的類取代了他的作用,那就是CLGeocoder類,使用該類進行反向解析也非常容易,請看下面代碼:
01CLGeocoder *geocoder=[[CLGeocoder alloc] init];02[geocoder reverseGeocodeLocation:newLocation 03 completionHandler:^(NSArray *placemarks,04 NSError *error)05 { 06 CLPlacemark *placemark=[placemarks objectAtIndex:0]; 07 NSLog(@"name:%@\n country:%@\n postalCode:%@\n ISOcountryCode:%@\n ocean:%@\n inlandWater:%@\n locality:%@\n subLocality:%@\n administrativeArea:%@\n subAdministrativeArea:%@\n thoroughfare:%@\n subThoroughfare:%@\n",08 placemark.name,09 placemark.country,10 placemark.postalCode,11 placemark.ISOcountryCode,12 placemark.ocean,13 placemark.inlandWater,14 placemark.administrativeArea,15 placemark.subAdministrativeArea,16 placemark.locality,17 placemark.subLocality,18 placemark.thoroughfare,19 placemark.subThoroughfare);20 }];
從代碼來看,CLGeocoder類沒有使用委託的形式通知返回狀態,而是通過block的方式進行回調,而且MKReverseGeocoder委託只返回了一個地標位置,但是CLGeocoder則返回了一個包含多個地標位置的數組,但這個數組在通常狀態下是只有一個元素,如果存在多個元素那證明了給定解析的經緯度被解析到多個不同的地標資訊。如果解析錯誤或者調用cancel方法則此參數為nil。
3、地圖顯示
想更加形象地表現出位置資訊靠文字的描述是遠遠不夠的,因為使用地圖來顯示地理位置將會給使用者帶來全新的體驗。在iOS裡面已經將Google地圖封裝到SDK裡面了,我們可以用很少的代碼來實現很多在地圖上的操作(如標記位置、繪畫線路等)。下面的代碼是產生一張地圖並顯示到介面上:
1- (void)viewDidLoad2{3 [super viewDidLoad];4 MKMapView *mapView=[[MKMapView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 460.0)]; 5 mapView.delegate = self;6 [self.view addSubview:mapView];7 [mapView release];8}
這夠簡單吧,上面的設定地圖委派物件是因為在下面要標記地理位置時需要用到的。那麼如何把取到的經緯度資訊顯示到地圖上呢?其實每個座標資訊在地圖中顯示後都對應一個MKAnnotationView,而MKAnnotationView又負責解析了一個實現MKAnnotation協議的資料對象。因此我們首先要做的事情就是把取到的經緯度轉換為MKAnnotation協議對象。先定義一個實現MKAnnotation協議的類:
01@interface DemoAnnotation : NSObject<MKAnnotation> {02 CLLocationCoordinate2D _coordinate;03}04 05-(id)initWithCoordinate:(CLLocationCoordinate2D)coordinate;06 07@end08 09@implementation DemoAnnotation10 11@synthesize coordinate=_coordinate;12 13-(id)initWithCoordinate:(CLLocationCoordinate2D)coordinate{14 if (self = [super init]) {15 _coordinate=coordinate;16 }17 return self;18}19 20-(void)setCoordinate:(CLLocationCoordinate2D)newCoordinate{21 _coordinate=newCoordinate;22}23 24-(NSString *)title{25 return @”我的位置”;26}27 28-(NSString *)subtitle{29 return nil;30}31 32@end
上面的類只是簡單地儲存了經緯度資訊。記得注意的是MKAnnotation協議中的title和subtitle的作用,如果你在顯示AnnotationView設定其canShowCallout屬性為YES時,則當使用者點擊AnnotationView時會彈出一個Callout視圖,用於顯示title和subtitle,假如設定title為nil那麼即使canShowCallout為YES也不會彈出Callout視圖。
接下來要改寫一下擷取定位成功後的方法,等待擷取定位成功後把經緯度設定到地圖上顯示。實現代碼如下:
01- (void)locationManager:(CLLocationManager *)manager02 didUpdateToLocation:(CLLocation *)newLocation03 fromLocation:(CLLocation *)oldLocation04{05 DemoAnnotation *annotation = [[DemoAnnotation alloc] initWithCoordinate:newLocation.coordinate];06 [_mapView addAnnotation:annotation];07 [annotation release];08 09 MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate]; 10 geocoder.delegate = self; 11 [geocoder start];12}
上述代碼中加粗部分的代碼就是把經緯度資訊封裝到剛才定義好的對象中。然後通過addAnnotation方法傳遞給MapView;這裡的_mapView是把之前viewDidLoad方法中的臨時變數改變為類屬性以達到跨方法引用的目的。經過上面步驟已經把我的位置引入到地圖裡面了,但現在還不會顯示在地圖上,因為還需要實現MapView中的協議,告訴MapView如何顯示你Annotation。以下代碼採用iOS中預設的圖釘樣式來顯示位置。如下:
01- (MKAnnotationView *)mapView:(MKMapView *)mapView02 viewForAnnotation:(id <MKAnnotation>)annotation03{04 NSString *annotationViewId=@"CurrentUserAnnotationView";05 MKPinAnnotationView *annotationView = (MKPinAnnotationView *)06 [mapView dequeueReusableAnnotationViewWithIdentifier:annotationViewId];07 if (annotationView==nil)08 {09 annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationViewId] autorelease];10 annotationView.canShowCallout = YES;11 }12 return annotationView;13}
上面的協議方法就是MapView告訴你他需要顯示哪個annotation,然後顯示的樣式由使用者你自己決定,但必須要繼承MKAnnotationView類。
到這裡對於定位和地圖的應用就告一段落了,當然關於 MKMapView 還有一些更加進階的特性
http://hi.baidu.com/wolf_childer/item/cc7c2bf7a2b88311fe3582f8