一、獲得用戶的位置
使用CoreLocation框架。
1、如果你的應用基於位置來正確運行,你應該在你的Info.plist中包括UIRequiredDeviceCapabilities鍵.App Store使用這個信息來阻止沒有定位的設備下載該應用。
UIRequiredDeviceCapabilities鍵對應一個字符串數組,包括:
1)location-services字符串--如果你請求一般的定位服務
2)gps--如果你請求GPS級的精度
如果你的應用需要定位,但是沒有定位也能正常運行,就不要包含這個鍵。
2、獲得用戶的定位
有2種方法:
1)標准的定位服務
2)顯著的位置變化定位服務:只在4.0以后有效。
3、確定定位服務是否可用:
1)用戶可以在設置程序中禁用定位服務
2)用戶可以對某個應用拒絕定位服務
3)設備可能在飛行模式,並且不能啟用需要的硬件。
因為這些原因,推薦你調用[CLLocationManager locationServicesEnabled]來確定定位服務是否可用。
使用[CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied 檢查定位服務是否對本app禁止。
4,啟動標准的定位服務:
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
// Set a movement threshold for new events.
locationManager.distanceFilter = 500;
[locationManager startUpdatingLocation];
5、啟動Significant-Change Location Service:
[locationManager startMonitoringSignificantLocationChanges];
啟動此服務后,即使應用沒有被啟動也可以接收通知,並且進入到后台狀態來處理事件。但是不能讓程序的后台任務超過10分鍾
6, 從服務中接收位置數據
locationManager:didUpdateToLocation:fromLocation
locationManager:didFailWithError:
7、監視Shape-Based Regions:
在iOS4.0以后,當用戶穿過地理邊界時應用可以使用region monitoring來被通知。這個通知即使應用沒有被啟動也可以接收,並且進入到后台狀態來處理事件。(推斷:應該和startMonitoringSignificantLocationChanges一樣,不能讓程序的后台任務超過10分鍾)
8、確定Region Monitoring的可用性
有幾種原因可能導致Region Monitoring可能不可用:
1)設備可能沒有硬件來支持region Monitoring
2)用戶可能禁用了定位服務
3)設備可能出於飛行模式。
因此,你需要調用[CLLocationManager regionMonitoringAvailable]和[CLLocationManager regionMonitoringEnabled]來確定其是否可用。
9、定義一個要監視的Region:
要監視一個區域,你需要定義個region並且注冊其到系統。Regions使用CLRegion類來定義,支持創建一個圓形區域。你創建的Region必須包括地理區域的數據和唯一的標志字符串。要注冊一個Region,你需要調用CLLocationManager對象的startMonitoringForRegion:desiredAccuracy:方法。
例子:
// 如果不支持RegionMonitoring,就不要創建Region了
if ( ![CLLocationManager regionMonitoringAvailable] ||
![CLLocationManager regionMonitoringEnabled] )
return NO;
// 如果半徑太大,注冊會自動失敗,因此當半徑太大時,將其設置為最大值
CLLocationDegrees radius = overlay.radius;
if (radius > self.locManager.maximumRegionMonitoringDistance)
radius = self.locManager.maximumRegionMonitoringDistance;
// 創建一個Region並開始監視它
CLRegion* region = [[CLRegion alloc] initCircularRegionWithCenter:overlay.coordinate radius:radius identifier:identifier];
[self.locManager startMonitoringForRegion:region desiredAccuracy:kCLLocationAccuracyHundredMeters];
[region release];
...
在注冊之后立即就開始了監視Region,但是不要奢望馬上就收到一個事件。只有穿過邊界時才會產生事件。因此,如果在注冊時,用戶的位置已經在區域內,locationManager不會產生事件的。
你必須明智的使用Regions Monitoring。Regions是一個系統分享資源,並且其數量是有上限的。因此,Core Location限制一個應用同時監聽的Regions數量。你應該考慮只監聽用戶臨近位置的Region,當用戶的位置變化時,你可以移除遠處的Regions並添加近處的Regions來監聽。如果你嘗試注冊一個Region並且沒有空間了,LocationManager會調用locationManager:monitoringDidFailForRegion:withError:方法,並傳遞一個kCLErrorRegionMonitoringFailure錯誤碼。
10、處理Region的穿過邊界事件
locationManager:didEnterRegion:
locationManager:didExitRegion:
11、在后台獲得定位事件
有幾種方法:
1)使用Signicant Location Change服務: [locationManager startMonitoringSignificantLocationChanges];
2)使用標准的定位服務: 在Info.plist中的UIBackgroundMode鍵中指定location
12、保護電池電量的技巧
1)在你不需要定位的時候關閉定位服務:
2)使用significant-change 定位服務
3)使用低精度的desired accuracy
4)如果是周期性的輪詢服務,那在周期中間關閉定位服務
二、獲得方向相關的事件
Core Location支持兩種方法來獲得方向相關的信息:
1)有磁力計(magnetometer)的設備可以報告設備指向的方向,也稱為heading。
2)有GPS的設備可以報告設備移動的方向,也稱為course。
記住,heading和course不表示相同的信息。
1、如果你的應用需要方向相關的信息才能正確運行,需要在Info.plist中指定UIRequiredDeviceCapabilities鍵,其值為一個字符串的數組,包括:
1)magnetometer---對應heading
2)gps--對應course
2、獲得Heading-Related事件:步驟:
1)實例化CLLocationManager對象
2)確定[CLLocationManager headingAvailable]
3)指定代理
4)如果你想獲得真實的north values,需要啟用標准服務
5)調用startUpdatingHeading開始投遞heading事件
如下例:
CLLocationManager* locManager = [[[CLLocationManager alloc] init] autorelease];
locManager.delegate = self;
// Start location services to get the true heading.
locManager.distanceFilter = 1000;
locManager.desiredAccuracy = kCLLocationAccuracyKilometer;
[locManager startUpdatingLocation];
// Start heading updates.
if ([CLLocationManager headingAvailable]) {
locManager.headingFilter = 5;
[locManager startUpdatingHeading];
}
代理函數:locationManager:didUpdateHeading:方法。
一旦你收到一個新的事件,你需要檢查headingAccuracy屬性來確保數據有效。另外,如果你正在使用true heading value,你應該在使用它之前,檢查其是否包含一個有效的值。
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
if (newHeading.headingAccuracy < 0)
return;
// Use the true heading if it is valid.
CLLocationDirection theHeading = ((newHeading.trueHeading > 0) ? newHeading.trueHeading : newHeading.magneticHeading);
self.currentHeading = theHeading;
[self updateHeadingDisplays];
}
3、在用戶移動時獲得course信息
擁有GPS硬件的設備可以生成設備的當前course和速度的信息。crouse信息用來指示設備正在移動的方向,並且不需要反應設備自身的方向。因此,主要用來導航。
實際的course和speed消息在CLLocation對象中返回.
newLocation.speed
newLocation.course
三、地理編碼位置數據 Geocoding Location Data:
1、關於Geocoder對象:
一個geocoder對象使用網絡服務來在(經度、緯度)和地標之間轉換,地標為一組數據的集合,例如街道、城市、州和國家信息。Reverse geocoding(逆地理編碼)是將(經度、緯度)轉換為地標。Forward geocoding(正地理編碼)是將位置名稱信息轉換為(經度、緯度)值。逆地理編碼在所有的iOS版本中都可用,但正地理編碼僅在iOS5.0以后支持。
因為地理編碼服務需要網絡,因此,如果設備出於飛行模式或者設備當前沒有網絡,地理編碼對象不能連接它需要的服務,因此肯定返回一個錯誤。
2、轉換坐標到Place Name信息:
在iOS中,你可以使用CLGeocoder類來處理逆地理編碼。MKReverseGecoder在iOS5.0以后被棄用。
3、使用CLGeocoder獲得地標信息:
創建一個CLGeocoder類的實例,並調用
[gecoder reverseGeocodeLocation:completionHandler:]方法
例子:
CLGeocoder *gecoder=[[CLGeocoder alloc]init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks,NSError *error){
if ([placemarks count]>0){
annotation.placemark=[placemark objectAtIndex:0];
...
}
}];
...
4、從Reverse Geocoder中獲得地標信息:
MKReverseGeocoder *theGeocoder=[[MKReverseGeocoder alloc]initWithCoordinate:location.coordinate];
theGeocoder.delegate=self;
[theGeocoder start];
代理函數為:
-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlaceMark:(MKPlaceMark *)place
-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error
5、轉換Place Names為Coordinate
[geocoder gecodeAddressString:@"1 Infinite Loop" completionHandler:^(NSArray *placeMarks,NSError *error){
for (CLPlacemark *aPlaceMark in placemarks){
//CLPlacemark對象包含地理位置信息,當然包括經緯度
}
}];
四、顯示地圖
Map Kit框架
1、理解Map Geometry
一個map view扁平的表現一個球形對象--地球。你需要了解一些關於如何在map view中指定點,並且這些點如何轉換為地球表面的坐標。如果你打算防止自定義的內容,如overlays,到地圖上,理解地圖坐標系統是非常重要的。
2、地圖坐標系統
地球的三維坐標如何被映射到地圖的二維坐標上的?
Map Kit使用Mercator地圖映射。
你如何指定地圖上的數據點取決於你打算如何使用它們。Map Kit支持3中基本的坐標系統來指定地圖數據點:
1)A map coordinate:是地圖的球形表示的(經度,緯度)。地圖坐標系主要的方法來指定地球上的位置。使用CLLocationCoordinate2D結構。使用MKCoordinateSpan和MKCoordinateRegion結構來指定區域。
2)A map point:是Mercator地圖映射之后的(x,y)點。使用地圖點來計算地圖相關的計算,而不是map坐標,因為他們簡化了計算。在應用中,你主要使用map point來指定shape和自定義的overlay的position。使用MKMapPoint結構。使用MKMapSize和MKMapRect結構來指定區域。
3)A Point是UIView坐標系中的graphical單元。在繪制view的內容之前,Map Piont和Map Coordinate必須被映射為Point。使用CGPoint,CGSize和CGRect。
在大多數情況中,你應該使用的坐標系取決於你要使用的Map Kit界面。當你要存儲實際的數據到文件中時,map coordinate是首選。
Core Location也是用map coordinate來指定位置值。
3、坐標系統間的轉換:
Convert From Convert To Conversion routines
Map Coordinate Points convertCoordinate:toPointToView:(MKMapView)
convertRegion:toRectToView:(MKMapView)
Map Coordinate Map Points MKMapPointForCoordinate
Map Points Map Coordinates MKCoordinateForMapPoint
MKCoordinateRegionForMapRect
Map Points Points PointForMapPoint:(MKOverlayView)
rectForMapRect:(MKOverlayView)
Points Map Coordinates convertPoint:toCoordinateFromView:(MKMapView)
convertRect:toRegionFromView:(MKMapView)
Points Map Points mapPointForPoint:(MKOverlayView)
mapRectForRect:(MKOverlayView)
4、添加一個Map View到用戶界面
MKMapView
5、配置Map的屬性
1)設置Map的可見部分:region屬性,是一個MKCoordinateRegion結構
typedef struct {
CLLocationCoordinate2D center;
MKCoordinateSpan span;
} MKCoordinateRegion;
有趣的是span。span類似於一個矩形的寬度和高度值,但是通過map coordinate指定,因此,通過degree(度)、minutes(分)、seconds(秒)來度量。latitude(緯度)的一度大約為111km,但是longitude(經度)的一度不同與緯度。在赤道,經度的一度大約為111km,但是在極點,這個值為0.如果你選擇使用米來指定span,你可以使用MKCoordinateRegionMakeWithDistance來創建一個region data 結構,用米而不是用度。
你指定的region屬性的值(或者使用setRegion:animated:方法)通常和實際存儲在屬性中的值不一樣。設置region的span只是定義了你想要查看的矩形,但也隱式設置了map view的縮放級別。map view不能顯示任意的縮放級別,並且必須調整你設置的region來匹配它支持的縮放級別。它選擇能容納你的整個region可見,同時又盡可能填滿屏幕的縮放級別。相應地,它然后調整region屬性。要查找region的結果而不實際更改region的屬性,你可以使用map view的regionThatFits:方法。
2)縮放和Panning 地圖內容:
a)Pan 地圖:更改centerCoordinate屬性的值 或者 setCenterCoordinate:animated:方法
b)更改縮放級別:更改region屬性的值或setRegion:animated:方法
如果只是要Pan地圖,需要使用第一種方法,如果你更改region屬性的center屬性,通常會導致縮放級別發生變化。
CLLocationCoordinate2D mapCenter = myMapView.centerCoordinate;
mapCenter = [myMapView convertPoint:CGPointMake(1, (myMapView.frame.size.height/2.0)) toCoordinateFromView:myMapView];
[myMapView setCenterCoordinate:mapCenter animated:YES];
要縮放地圖,修改region的span,要zoom in,就設置一個更小的值給span,要zoom out,就設置一個更大的值給span。
MKCoordinateRegion theRegion = myMapView.region;
// Zoom out
theRegion.span.longitudeDelta *= 2.0;
theRegion.span.latitudeDelta *= 2.0;
[myMapView setRegion:theRegion animated:YES];
3)在地圖上顯示用戶的當前位置
設置showsUserLocation屬性為YES。這樣做導致map view使用Core Location來查找用戶的位置並添加一個MKUserLocation類型的annotation到地圖上。
6、響應用戶與地圖的交互
MKMapView報告顯著的map-related事件給其代理。代理遵循MKMapViewDelegate協議。可以響應如下事件:
1)更改地圖的可見region
2)從網絡上加載map 碎片
3)更改用戶的位置
4)更改相關的annotation和overlay
五、Annotating Maps
1、添加Annotations到地圖上:
要實現這個必須提供兩個對象:
1)遵守MKAnnotation協議的對象(即annotation對象)
2)一個繼承自MKAnnotationView類的視圖,用來繪制annoatation的表現(及annotation view)
Map Kit提供了一些標准的annotation視圖,並且你可以定義自己的annotation views。
2、添加Annotation到地圖上的清單:
1)定義一個合適的annotation對象,使用下面的選項:
a)使用MKPointAnnotation類來實現一個簡單的annotation。
b)定義一個自定義的遵循MKAnnotation協議的對象
2)定義一個annotation view來表示屏幕上的數據。
a)如果annotation可以被一個靜態圖像代表,那就創建一個MKAnnotationView類的實例,並指定image屬性
b)如果你想使用一個標准的annotation,那就創建一個MKPinAnnotationView類的實例
c)如果靜態圖像不合適,那就子類化MKAnnotationView,並實現自定義的draw代碼。
3)實現mapView:viewForAnnotation:方法---在你的mapView代理中。並返回一個annotationView。
4)使用addAnnotation:或addAnnotations:方法來添加addnotation對象。
3、定義自定義的Annotation對象
主要是遵守MKAnnotation協議:
必須有coordinate只讀屬性
4、使用標准的Annotation Views:
MKAnnotationView 和 MKPinAnnotationView(MKAnnotationView的子類)
MKAnnotationView適用於只展示一幅圖像。通過image屬性,還可以指定centerOffset屬性來移動中心點。
5、定義自己的Annotation View
子類化MKAnnotationView,你可以繼續使用image屬性,並重載drawRect:方法。
6、在代理中創建Annotation Views
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation{
}
7、管理地圖的Annotation對象:
可以在mapView:regionWillChangeAnimated:和mapView:regionDidChangeAnimated:方法中添加和移除annotations
在iOS4.0以后,你可以使用MKMetersBetweenMapPoints方法來獲得兩個點之間的絕對距離。你還可以使用MKMapRectIntersetsRect函數來查找任何的交集。
8、標記你的AnnotationView為可拖動:
1)在你的annotation對象中,實現setCoordinate:方法來允許map view更新annotation的coordinate點。
2)設置其draggable屬性為YES。
當用戶拖動annotation時,委托收到mapView:annotationView:didChangeDragState:fromOldState:方法
9、在地圖上顯示OverLay:
和Annotation類似:
1)需要實現了MKOverLay協議的對象(即overlay對象)
2)一個視圖(繼承自MKOverlayView類)(即overlay視圖)
10、添加overlay到地圖的清單:
1)定義一個overlay對象:
a)使用MKCircle、MKPolygon、MKPolyline類 等
b)子類化MKShape或MKMultiPoint來創建自定義的overlay對象。
c)使用一個已存在的類並遵守MKOverlay協議。
2)定義一個overlay視圖:
a)對於標准圖形,使用MKCircleView、MKPolygonView、MKPolylineView
b)對於繼承自MKShape的自定義形狀,定義合適的MKOverlayPathView的子類來渲染形狀。
c)對於其它自定義的形狀和overlay,子類化MKOverlayView並實現drawRect:方法
3)實現代理函數: mapView:viewForOverlay: 並返回MKOverlayView
4)使用addOverlay:方法添加overlay
11、使用標准的Overlay對象和視圖:
MKCircle、MKPolygon、MKPolyline為overlay對象。
MKCircleView、MKPolygonView、MKPolylineVIew為overlay視圖。
12、定義自定義的Overlay對象:
子類化MKShape或MKMultiPoint或者采用MKOverlay協議。
1)必須有coordinate可讀屬性
2)一個bounding rectangle,完全包含了overlay的內容。
13、定義自定義的Overlay視圖:
子類化MKOverlayView,並重載
drawMapRect:zoomScale:inContext:
canDrawMapRect:zoomScale:
14、從代理中創建Overlay視圖
mapView:viewForOverlay:方法
15、管理地圖的Overlay對象:
overlays屬性
代理函數mapView:didAddOverlayViews:方法,然后使用MKMapRectIntersectsRect函數來看看已經添加的overlay與其它的overlay是否有交集。
16、使用overlay當作annotations
MKOverlay協議遵循MKAnnotation協議。因此,所有的overlay對象都可以當作annotation對象來使用。