2012年7月23日 星期一

實作 IOS 定位服務

剛玩定位這玩意,參考了好幾位大師的範例和說明,然後再實作、整理出來的筆記。

參考IOS 定位範例:


1.       iPhone開發實戰定位與地圖


2.       GPS 衛星定位展示範例



3.       Map Kit (part3)





實作IOS定位服務


關於定位服務,IOS中已經提供二個framwork給開發者使用:

1.          CoreLocation.framework

n   (CLLocationManager類別)定位-獲得經緯度。

2.      MapKit.framework

n   (MKReverseGeocoder 類別)當前的經緯度所對應的地理位置資訊是什麼。

n   (MKAnnotationView類別)把取到的經緯度資訊顯示到地圖上



Xcode新建專案:

.h檔裡要宣告這是一個符合MKMapViewDelegate, MKReverseGeocoderDelegate, CLLocationManagerDelegate Protocal的類別。

1. MKMapViewDelegate:委派代理地圖呈現

2. MKReverseGeocoderDelegate:委派理取得地理位置資訊

3. CLLocationManagerDelegate:委派代理取得當前的經緯度。



LocationViewController.h

#import <UIKit/UIKit.h>

#import <CoreLocation/CoreLocation.h>

#import <CoreLocation/CLLocationManagerDelegate.h>

#import <MapKit/MapKit.h>

#import <MapKit/MKReverseGeocoder.h>

@interface LocationViewController : UIViewController<MKMapViewDelegate, MKReverseGeocoderDelegate, CLLocationManagerDelegate> {

    CLLocationManager   *locmanager;

    MKMapView *mapView;

}

@end

.m檔裡要實作定位服務的功能,記得將delegate設為self,程式碼如下:

LocationViewController.m

#import "LocationViewController.h"

#import "DemoAnnotation.h"

@interface LocationViewController ()

@end


@implementation LocationViewController

- (void)viewDidLoad {

    [super viewDidLoad];

// 加入MKMapView 的物件,可呈現地圖上的所有資訊。

    mapView=[[MKMapView alloc] initWithFrame:self.view.frame];

    mapView.delegate=self;

    [self.view insertSubview:mapView atIndex:0];

    // CLLocationManager 物件,startUpdatingLocation 可以

    locmanager = [[CLLocationManager alloc] init];

        [locmanager setDelegate:self];

        [locmanager setDesiredAccuracy:kCLLocationAccuracyBest];        

    //透過呼叫startUpdatingLocation開啟定位功能,然後使用stopUpdatingLocation停止定位,其中定位資訊是通過loctionManager:didUpdateToLocation

        [locmanager startUpdatingLocation];

}


- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation

{

      // newLocation是新偵測到的地理位置,針對此地設定區域範圍

            MKCoordinateRegion region;

            region.center=newLocation.coordinate;

            //使用 Span 設定地圖的縮放。

            MKCoordinateSpan span;

            span.latitudeDelta=.005;

            span.longitudeDelta=.005;

            region.span=span;

            [mapView setRegion:region animated:TRUE];

    // DemoAnnotation是繼承MKAnnotation的自訂類別,稍後會提到,可以記錄關於此座標點的相關資訊,像主題及副標題。

    DemoAnnotation *annotation = [[DemoAnnotation alloc] initWithCoordinate:newLocation.coordinate];

        // 將座標資訊加入地圖

    [mapView addAnnotation:annotation];

    //

    // 若不想用自訂的格式可以直接使用MKReverseGeocoder類別,如下:

    MKReverseGeocoder *geocoder=[[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate];

            geocoder.delegate=self;

            [geocoder start];

    */

}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {

}

這裡先補充newLocation表示最新定位,oldLocation表示上一次的定位資訊。這兩個都是CLLocation對象。以下是CLLocation的屬性說明:

altitude 海拔高度
coordinate
經緯度
course
行駛方向
horizontalAccuracy
水準方向的精確度
Speed
行駛速度
timestamp
時間戳記
verticalAccuracy
垂直方向的精確度

didUpdateToLocation事件的處理,若不想用自訂的格式,是使用MKReverseGeocoder類別,就要實作didFindPlacemarkdidFailWithError,可以取得經緯度的地理資訊,例如:國家、郵地區號..



- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark{

    NSLog(@"\n country:%@\n postalCode:%@\n ISOcountryCode:%@\nlocality:%@\n subLocality:%@\n administrativeArea:%@\nsubAdministrativeArea:%@\n thoroughfare:%@\n subThoroughfare:%@\n",

          placemark.country,

          placemark.postalCode,

          placemark.ISOcountryCode,

          placemark.administrativeArea,

          placemark.subAdministrativeArea,

          placemark.locality,

          placemark.subLocality,

          placemark.thoroughfare,

          placemark.subThoroughfare);

    // 將座標資訊加入地圖

    [mapView addAnnotation:placemark];

}

-  (void)reverseGeocoder:(MKReverseGeocoder*)geocoder didFailWithError:(NSError*)error{

    NSLog(@"reverse geocoder fail!!");

}


[地圖顯示] - 如何把取到的經緯度資訊顯示到地圖上呢?其實每個座標資訊在地圖中顯示後都對應一個MKAnnotationView,而MKAnnotationView又負責解析了一個實現MKAnnotation協定的資料物件。因此我們首先要做的事情就是把取到的經緯度轉換為MKAnnotation協定物件。先定義一個實現MKAnnotation協議的類:

DemoAnnotation.h

#import <Foundation/Foundation.h>

#import <MapKit/MapKit.h>


@interface DemoAnnotation : NSObject<MKAnnotation> {

    CLLocationCoordinate2D _coordinate;

}

-(id)initWithCoordinate:(CLLocationCoordinate2D)coordinate;

@end



DemoAnnotation.m

#import "DemoAnnotation.h"

@implementation DemoAnnotation

@synthesize coordinate=_coordinate;

-(id)initWithCoordinate:(CLLocationCoordinate2D)coordinate{

    if(self = [super init]) {

        _coordinate=coordinate;

    }

    return self;
}
-(void)setCoordinate:(CLLocationCoordinate2D)newCoordinate{

    _coordinate=newCoordinate;

}

-(NSString*)title{

    return @"我的位置";
}
-(NSString*)subtitle{

    return @"Peggy haha";
}

@end




呼叫[mapView addAnnotation:XXX] 會實作mapView 協定中的 viewForAnnotation didReceiveMemoryWarning

viewForAnnotation這個方法,這是MKMapView實際建立座標點的地方。MKMapView類別在render地圖的時候會依照Annotation集合的資料建立座標點。

Annotation集合中有幾筆資料vieForAnnotation方法就會被執行幾次。因此每次viewForAnnotation被執行,我們都要建立一個MKAnnotationView物件的實體,並且return這個實體。 MKMapView接收到MKAnnotationView的實體就會將它顯示在地圖上,這就是我們想要顯示在地圖上的座標點。在上面程式碼中使用了 MKPinAnnotationView這個物件是繼承自MKAnnotationView,作用就是在地圖上顯示一個大頭釘。



LocationViewController.m



-  (MKAnnotationView *)mapView:(MKMapView *)mapView

             viewForAnnotation:(id <MKAnnotation>)annotation{

    NSString*annotationViewId=@"CurrentUserAnnotationView";

    MKPinAnnotationView *annotationView= (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationViewId];

    if(annotationView==nil)

    {

        annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationViewId];

        //地圖上的大頭針自訂格式

        annotationView.image = [UIImage imageNamed:[NSString stringWithFormat:@"12.png"]];       



        annotationView.draggable = NO;

        //annotationView.animatesDrop = YES; //->目前只有預設的大頭針才有動畫效果。

//設定在點選大頭釘的時候氣泡視窗是否會談出來。

        annotationView.canShowCallout = YES;

       

        //設定右邊的箭頭鈕及事件

        UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];  

        [button addTarget:self action:@selector(checkButtonTapped:event:) forControlEvents:UIControlEventTouchUpInside];  

        annotationView.rightCalloutAccessoryView=button;  

       

        //設定左邊的小圖示及事件

        UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeContactAdd];

        [leftButton setImage:[UIImage imageNamed:@"star.png"] forState:UIControlStateNormal];

        [leftButton addTarget:self

                       action:@selector(checkButtonTapped:event:)

             forControlEvents:UIControlEventTouchUpInside];

        annotationView.leftCalloutAccessoryView = leftButton;

    }

    return annotationView;
}

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

}

- (void)checkButtonTapped:(id)sender event:(id)event{  

    UIAlertView *tmp= [[UIAlertView alloc] initWithTitle:@"訊息!" message:@"Peggy測試" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];  

    [tmp show];  

}  



程式執行結果:


  

2 則留言:

  1. Hi there Dear, are you really visiting this website on a regular basis, if
    so afterward you will without doubt take fastidious knowledge.


    My web-site http://liverwiki.hupo.org.cn

    回覆刪除
  2. Thanks for the marvelous posting! I certainly enjoyed reading it, you are
    a great author.I will make certain to bookmark your blog and may
    come back very soon. I want to encourage you to continue your
    great job, have a nice afternoon!

    Feel free to visit my site ... es.jaykodesign.com

    回覆刪除