見出し画像

Apple Mapチュートリアル - 第5回:マップをスクロールするたびに近くを検索するアクションを作る

前回までの実装で、マップ上で自分の近くのカフェを表示できるようになりましたが、近くに行きたいカフェがない場合、少しマップをスクロールして、他の場所にカフェがないか検索できる機能をつけたくなることはよくあると思います。

マップがスクロールするたびに、近隣のものを再検索

という挙動はいろんなアプリで頻繁にでてくる挙動です。
今回はこの機能の作り方を説明していきます。

前回、ViewControllerに実装したプロトコル、MKMapViewDelegateには、ユーザーのマップの操作を受け取るデレゲートメソッドがあります。

func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {

この関数を実装すると、ユーザーがマップをスクロールしてマップの表示領域が変わるたびに、この関数でそのイベントを受け取れるようになります。

この関数のなかで、下のように、前回実装したsearchNearby()を呼び出してあげるだけで、マップスクロール -> 再検索が実現できます。

func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
   //新しいエリアをサーチする
   searchNearby(region: mapView.region)
}

結果は下のようになります。自分の位置のピンがない場所でもカフェの検索ができるようになりました!

スクリーンショット 2020-11-28 13.07.42


ズームアウトしすぎるとどうなるのか

さて、この状態でマップをズームアウトしていってみてください。
下のように、日本地図が見えるほどズームアウトしても、ピンが表示されています。

スクリーンショット 2020-11-28 12.26.30

MKLocalSearchはデフォルトで10件しかヒットしないので、このようになっていますが、例えば検索数に制限のないAPIの場合、日本全国にピンがたちマップが非常に重くなります。また従量課金のAPIを使っている場合や自前でAPIを用意している場合はサーバーのデータ転送量も膨大になってお金がかかってしまいます。

ユーザーにとっては、日本全体が表示された状態で狙いのお店をタップできるわけではないのでUX的にも地図が広域を表示しているときはピンを表示しない(=検索を行わない)というロジックをいれると、UX的にもメリットがあります。

そのためには、先程のregionDidChangeAnimatedのなかで、searchByLocation()mapView regionDidChangeAnimatedのなかで呼ぶ前に、
下のように、mapView.region.spanというデータをチェックします。このなかに、マップの縦方向の表示領域の緯度の差分(latitudeDelta)と、横方向の表示領域の経度の差分(longitudeDelta)を取得できます。これがある一定以上だったら、マップ上のピンをすべて削除するというコードを追加しました。

func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {  
   let span = mapView.region.span
   print("latitudeDelta:\(span.latitudeDelta), longitudeDelta:\(span.longitudeDelta)")
   if span.latitudeDelta > 0.1044790983605779 || span.longitudeDelta > 0.07457650057381215{
       for annot in mapView.annotations{
           mapView.removeAnnotation(annot)
       }
   }else{
       //新しいエリアをサーチする
       searchNearby(region: mapView.region)
   }
}

上のコードでは、print()関数で、latitudeDeltalongitudeDeltaをログに出力するようにしたので、この値をみながら、自分のアプリでどの数値より大きくなったらサーチをやめてピンを非表示にしたいのかを考えて適切な値を探してみてください。

これでズームアウトすると、下のようにカフェのピンが消えるようになりました↓↓↓

画像4

もちろん、再びズームすると、mapView regionDidChangeAnimatedのなかで再び検索が行われピンが表示されます。

画像3

このように少しの工夫でUXを改善し、かつ、検索の負荷を軽減することができました。
従量課金の検索APIや自前のサーバーを使っている場合はこれを入れておかないと、ユーザー数が増えた時に大変なことになるので、是非とも実装に入れてみてください。

今回は、

ズームアウトする → 検索をやめてピンを非表示にする

という実装だけにとどめましたが、実際のプロダクトでは、このときに

「地図が広域なため、検索が実行できません」

「地図をズームして***を探してね」

のようなラベルやスナックバーのようなものでユーザーの行動を促してもよいと思います。

まとめ

- MKMapViewDelegateのregionDidChangeAnimatedを実装し、ユーザーのスクロールイベントを受け取る

- regionDidChangeAnimatedのなかで現在のマップが表示しているエリアを検索する

- 地図が広域になりすぎたとき(=検索エリアが広くなりすぎた時)は、検索をやめピンの表示もやめる

このチュートリアルのソースコードは↓↓↓に置いてあります。
(GitHub上で☆をつけていただくと励みになります!)

https://github.com/mizutori/iOSMapStarterKit

ここまでのコードは、コミットハッシュ
c83309ed64ad29bfd5234b12d5c45b1724beadf1
でコミットしてあります。下のコマンドでロールバックして見てみてください。

git checkout c83309ed64ad29bfd5234b12d5c45b1724beadf1

このブログに関する質問やiOSアプリの開発の相談はこちらから↓↓↓

@mizutory
mizutori@goldrushcomputing.com


この記事が気に入ったらサポートをしてみませんか?