Objective-CでMKMapViewの緯度経度を取得とドラッグイベントを制御する方法

先日、objective-C版のGeoHexをGithubにアップしてみた(http://d.hatena.ne.jp/seikoudoku2000/20110425)。実装にあたっての感想を少々。

エンコード部分に関しては、javascript版やJava版が既にある(自分はJavaが一番経験が長い。)ので、そっちを元に同じように実装していけば、何とかなるのでは無いかと思っていて、大体、objective-Cの経験の浅さから結構苦労しましたが、、今いちなコードなんだろうなというのを差し引いて、概ねその通りにいきました。(ただ、提供されているテストケースのテキストファイルを読み込んで、各行毎にassertしていくというテストの書き方が分からず、それが実装できていません。。かなり大きな課題です。)

プラスアルファで、やってみたいなと思ったのは、yahooのyubichizライクな描画をGeoHexで使ってやるということです。
yubichizの場合は線を描画してから、その線沿いの店を検索しますが、ここにどういうアルゴリズムが当てはめられているのか知る由もありませんが、それなりにコストのかかる計算や検索であることは想像がつきます。
でも、この線をGeoHexに置き換えれば、事前に施設毎に属するGeoHexのコードを付与しておくことで、単純なクエリを用いて該当の施設情報を検索することができます。(SELECT * FROM spot_table WHERE hex_level_6 = 'xxx' 的な感じ。)
前にjavascriptで挑戦してみましたが、ブラウザのレンダリング処理の限界なのか分かりませんが、指のなぞる操作と六角形が描画との時差があり、何かいまいちな感じにしか仕上がりませんでした。
これをネイティブのobjective-Cでやれば、この時差が解消され、もうちょっといけてる感じになるのでは無いかと思い、試してみたかっという感じです。

前置きが長くなりましたが、それにあたって、表題のことを実装する必要がでてきました。ざっくりと必要な機能は2つ。

  • MKMapViewでタッチしている地点の緯度経度を取得する。
  • MKMapViewにtouchMoveイベントを紐づける + 地図のドラッグとのモード切り替えにより両方の操作が行えるようにする。(yubichizと同様の形式。)

ググって色々と調べてみたのですが、これだ!という解は見つからなかったので、自分のやり方をメモしておきます。
結論としては、

  • MKMapViewで提供されている関数を使う。
  • UIPanGestureRecognizerを使う。GeoHexを描画したい時は地図スクロールを不可+UIPanGestureRecognizerを有効にし、地図自体をドラッグさせたい時は地図しクロールを可+UIPanGestureRecognizerを無効にする。

コードの該当部分を抜粋するとこんな感じ。(モノはGithub上にアップしています。)

- (void)viewDidLoad {
    [super viewDidLoad];	
	mapView_.delegate = self;
	MKCoordinateRegion region = mapView_.region;
	region.span.latitudeDelta = 0.05; // 地図の表示倍率
	region.span.longitudeDelta = 0.05;
	region.center = CLLocationCoordinate2DMake(35.658517, 139.701334); //near Shibuya,Tokyo
	[mapView_ setRegion:region animated:YES];
	
	UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];  
	[mapView_ addGestureRecognizer:tapGesture];  
	[tapGesture release];  

	panGesture = [[UIPanGestureRecognizer alloc] 
				  initWithTarget:self 
				  action:@selector(handlePanGesture:)];
	level = [[levelLabel text] intValue];
	hexCodeSet = [[NSMutableSet set] retain];
	polyArray = [[NSMutableArray alloc] init];
}


- (void) handlePanGesture:(UIPanGestureRecognizer*)sender {  
	CGPoint location = [sender locationInView:mapView_];
	CLLocationCoordinate2D mapPoint = [mapView_ convertPoint:location toCoordinateFromView:mapView_];
	[self drawHex:mapPoint.latitude lon:mapPoint.longitude level:level];
} 


- (void) handleTapGesture:(UITapGestureRecognizer*)sender {  
	CGPoint location = [sender locationInView:mapView_];
	CLLocationCoordinate2D mapPoint = [	mapView_ convertPoint:location toCoordinateFromView:mapView_];
	[self drawHex:mapPoint.latitude lon:mapPoint.longitude level:level];
} 

-(IBAction)changeMode:(UISegmentedControl *)sender {
	if ([sender selectedSegmentIndex] == 0) {
		mapView_.scrollEnabled = TRUE;
		[mapView_ removeGestureRecognizer:panGesture];
	} else {
		mapView_.scrollEnabled = FALSE;
		[mapView_ addGestureRecognizer:panGesture];  
	}
}


後者に関して、何でobjcetive-Cの素人のくせにGestureRecognizerというものに気付いたかというと、oreillyのProgramming iOS4(現時点ではearly release)という本に手を出していたから。
oreillyのdeal of the dayで半額になってて、何かレビューが良かったので購入してみて、epub版をiPadに入れてコツコツと読み進めていたのが役に立ちました。(Reference的な所は読み飛ばしたままですが。)というか、そういうことをやりたいなーと思いながら、この本を読んでいたので、GestureRecognizerなんてものがあるのか!ということに気付けました。けっこう嬉しかったです(笑)。
ちなみに、この本ではなかなかobjective-Cのコードが出てこず、C言語の説明、オブジェクト指向言語の説明などが、最初のほうに延々と続き、最初はおいおいと思ったのですが、意外にもその部分が凄く読み応えがあって面白かったです。ポインター、オブジェクトとクラス、メッセージ、などなどの概念が筋道を立てて懇切丁寧に説明されていて、かなり勉強になりました。日本語化されたら、オブジェクト指向言語の説明として、新人教育に使ってもいいのではないかと思ったり。

明日は、この記事を英語に翻訳だ!
多分。。


一応やってみた。
http://d.hatena.ne.jp/seikoudoku2000/20110506