なんとなく

忘備録です

Didload でMap 生成後にピン表示とかさせたいメモ

MAPを表示した後、ピンを立てたり、現在地を表示させる という時、
単純に - (void)viewDidLoad の中で、ピン表示用の住所から緯度経度取得→ピンセット
とさせてみたけど、うまくいかない。

デバッグすると、そもそも 緯度経度変換の処理で、緯度経度が取得できてない
という悲しさ。直接、緯度経度をセットしてあげるとできるんだけど、
なぜか、データ取得処理とかすると、うまくいかない…orz
想像するに、MkMapが生成される前に、緯度経度取得処理をしようとするから
ダメじゃね? と思って、探してみた。

わからん。なら、本家本元のガイド見てみる。

☆Location Awareness Programming Guide
https://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/LocationAwarenessPG/Introduction/Introduction.html


そうね、多分、MAPのDelegateメソッドのリファレンスよね てことで、

☆MKMapViewDelegate Protocol Reference
https://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapViewDelegate_Protocol/MKMapViewDelegate/MKMapViewDelegate.html#//apple_ref/doc/uid/TP40008204

お!MAPデータがロードされた時ってある!(名前的に)

Loading the Map Data
– mapViewWillStartLoadingMap:
– mapViewDidFinishLoadingMap: ーーーー>コレ!
– mapViewDidFailLoadingMap:withError:

でも、コレじゃダメみたいだった…orz

どうやら、位置情報取得処理(Geocode)が別スレッドで走って、ピンを立てる処理が先に
走ってしまい、位置情報が無いのでピンが立たない ということらしい。

for (ピンを立てたい住所分ループ)
{
    1) 住所の有無チェック
    2) 位置情報取得
    3) ピンを立てる
}

2) の処理が終わってないのに、3) の処理が実行されるので、
位置情報が無い=ピンが立たない ってこと。

調べてみると、処理が終わったよ という通知処理を行なって、
通知を受け取ったら、処理を実行する
 という機能があるんだって!!

ほー!NSNotification という機能。

iPhone開発虎の巻
http://iphone-tora.sakura.ne.jp/nsnotification.html

☆通知と通知センター
http://www15.plala.or.jp/NovemberKou/programming/columnHome/column/whatIsNotification.html


要するに、2) の処理が終わったよ という通知を受け取ったら、3) の処理を行う
という形に変えたら良いわけね。

1) NSNotificationCenter に登録時、通知を受信した時の処理(メソッド)を指定する。

// Noticifation登録用オブジェクト生成
NSNotificationCenter* center = [NSNotificationCenter defaultCenter]
// "KEY_GEOCODE_OK"という名前の通知が来たら、"addPlacemark:"という処理を実行してね と登録。
[center addObserver:self selector:@selector(addPlacemark:)
      name:KEY_GEOCODE_OK object:nil];


2) 通知の実施処理

// NSNotificationを作成する
NSNotification* notification = [NSNotification notificationWithName:sResult
object:self userInfo:dicPlaceObj];
// NSNotificationCenterを取得する
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
// 通知を行う
[center postNotification:notification];


3) 通知受け取った時の処理

– (void) addPlacemark:(id)sendor
{

}


ということで、以下のようにしたら 成功!


MapViewController.m

@synthesize mapView;
CLPlacemark *antPlacemark;

#define KEY_GEOCODE_OK @"geoCodeOk"
#define KEY_GEOCODE_NG @"geoCodeNg"
#define KEY_PLACEMARK @"placemark"

- (void)viewDidLoad
{
    [super viewDidLoad];
    // 位置情報取得通知登録
    [self entryNotificationGeocode];
    // 位置情報の利用有無チェック
    [self checkMapLocation];
    // 初期画面設定
    [self setInitView];
    // selector内で指定した時間後に住所データから、地図にピン表示を行う
    [self performSelectorOnMainThread:@selector(createVisitAndLocation)
                           withObject:nil waitUntilDone:YES];
}

#pragma mark - DidLoad

// 位置情報取得通知の登録
- (void) entryNotificationGeocode
{
    // NSNotificationCenterを取得
    NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
    // 位置情報取得完了(OK)をObserverとして登録
    [center addObserver:self selector:@selector(addPlacemark:)
                   name:KEY_GEOCODE_OK object:nil];
    // 位置情報取得完了(NG)をObserverとして登録
    [center addObserver:self selector:@selector(alertPlacemark:)
                   name:KEY_GEOCODE_NG object:nil];
}

// 位置情報取得の通知
- (void) notificateGeocodeDone:(NSString *)sResult:(NSMutableDictionary *)dicPlaceObj
{
    // NSNotificationを作成する
    NSNotification* notification = [NSNotification notificationWithName:sResult
                                                                 object:self userInfo:dicPlaceObj];
    // NSNotificationCenterを取得する
    NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
    // 通知を行う
    [center postNotification:notification];
}


// 通知により受け取った位置情報で、Annotationを追加する。
- (void) addPlacemark:(NSNotification *)notification
{
    // Annotation追加患者データ
    NSMutableDictionary *dicPatient = [notification userInfo];
    
    // Mapにピン設定
    [self setAnnotation:dicPatient];
}

// ログイン取得データから訪問予定住所の取得と位置情報取得
- (void) createVisitAndLocation
{
    // 指定時間スリープ(3秒)
    [NSThread sleepForTimeInterval:3];
    // 住所情報の取得
    [self setAddrData];    // NSMutableDictionary *dicPatient に、key=@"住所" のValueをセット。
    
    self.mapView.showsUserLocation = YES;
    
    // 位置情報取得と通知
    [self geocodeAddrAndNortificate];
}

- (void) checkMapLocation
{
//    locationManager = [[CLLocationManager alloc] init];
    
    // 位置情報サービスが利用できるかどうかをチェック
    if ([CLLocationManager locationServicesEnabled]) 
    {
        locationManager.delegate = self;
        // 測位開始
        [locationManager startUpdatingLocation];
        NSLog(@"Location services OK");
    }
    else
    {
        NSLog(@"Location services not available.");
    }
}

// 住所の緯度経度取得&終了通知
- (void) geocodeAddrAndNortificate
{
    for (int i = 0; i < [patientArray count]; i++)
    {
        // NSMutableDictionary *dicPatient = obj;
        NSMutableDictionary *dicPatient = [patientArray objectAtIndex:i];
        // 住所データ有りの場合
        if (([dicPatient.allKeys containsObject:@"住所"]) && (![[dicPatient objectForKey:@"住所"] isEqual:[NSNull null]]))
        {
            // 位置情報取得後、完了通知
            [self geocode:dicPatient];
        }
    }
}

// 住所から経度緯度を算出(dicPatient = @"住所"有りのみ)
- (void) geocode:(NSMutableDictionary *)dicPatient
{
    // 住所
    NSString *addressString = [dicPatient objectForKey:@"住所"];
    
    // ロケーション生成
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder geocodeAddressString:addressString
                 completionHandler:^(NSArray* placemarks, NSError* error)
     {
        // 位置情報取得OK
        if ((placemarks != nil) && ([placemarks count] > 0))
        {
            // 取得した位置情報を追加(先頭データ)
            CLPlacemark *placemark = [placemarks objectAtIndex:0];
            [dicPatient setValue:placemark forKey:KEY_PLACEMARK];
            
            // 位置情報取得完了を通知
            [self notificateGeocodeDone:KEY_GEOCODE_OK :dicPatient];
        }
        else
        {
            // 位置情報取得完了を通知
            [self notificateGeocodeDone:KEY_GEOCODE_NG :dicPatient];
        }
    }];
}


長かった…

※GCは、Autoモードにしてるので、記述してないです。
今後…