WWDC23:Meet MapKit for SwiftUIの日本語訳です。
MapKitのSwiftUIサポートが拡張されたことで、マップ(地図)をアプリに統合することが簡単になりました!SwiftUIを使用してマップに注釈(annotations)やオーバーレイ(overlays)を追加したり、カメラを制御したりする方法を説明します。
大幅に拡張されたSwiftUI APIにより、すべてのプラットフォームのアプリにマップを統合することが、これまで以上に簡単になりました。
どれだけ簡単かをお見せするために、フル機能の旅行計画アプリをゼロから作ってみましょう。
一日の計画を立てるのに役立つアプリを作りましょう。 注釈(annotations)を使って地図上の場所をマークしたいです。
マップ上の各マーカーをタップして、その場所についてもっと詳しく知ることができるようにします。 Look Around(周囲を見渡す機能)を統合して、私たちが訪れたいと思うような場所を探索します。
ビーチまでのドライブルートを示すオーバーレイ(ルートの表示)を追加しよう。※overlayで重ね合わせるの意味なので、マップ上にルートを上書きして表示する。
マップを使って、さまざまな場所や地域を表示します。
リアルな標高を可能にすることで、地図に別の次元を加えることにします。
衛星画像やflyover(フライオーバー:遊覧飛行)画像を表示する方法も紹介します。
自分がどこにいるのか分かるように、ユーザーの位置情報アイコンなど、いくつかのコントロールを地図に追加する。
お話したいことがたくさんあります!
さっそく取り掛かりましょう!新しいSwiftUIプロジェクトから始めましょう。
MapKitを使います。
そしてMapを追加する。
たった1行のコードでインタラクティブな地図ができました!
ボストン・コモンはあらゆるものの真ん中にある美しい公園で、ウォーキング・ツアーの素晴らしい出発点になるだろう。
目次
- 1 Show content on the map(地図上にコンテンツを表示する)
- 2 Give the user a sense of place(ユーザーに場所を感じさせる)
- 3 Search for places(場所を探す)
- 4 Display a place or region(場所または地域を表示する)
- 5 Control what the map displays(地図の表示内容をコントロールする)
- 6 Search in the visible region(可視領域での検索)
- 7 Display useful search-result information(有用な検索結果情報の表示)
- 8 Show overlay content(オーバーレイ・コンテンツを表示する)
Show content on the map(地図上にコンテンツを表示する)
まず最初にすることは、駐車場を示すコンテンツを地図に追加することだ。
駐車場にアクセスするために車が使うスロープの真上、歩き始めるために乗るエレベーターの近くに印をつける。
マーカーとアノテーションを使って、地図上の特定の座標にコンテンツを表示する方法を学びます。
では、車を停めて歩き始めよう!
MapContentBuilder クロージャを使ってマップにマーカーを追加してみます。
かっこいい!SwiftUIに慣れている場合、マップにマーカーを追加することはリストにビューを追加することと同じように感じます。
※CLLocationCoordinate2Dが位置情報を示す構造体ですが、これにextensionしてparkingプロパティを追加することで、駐車場の位置情報を静的に設定している。
地図がMarker(マーカー)を表示するためにズームインすることで、私たちのコンテンツを自動的にフレーミングしている様子をご覧ください。
Marker(マーカー)とは何でしょうか?コンテンツビルダーを使って表示できるコンテンツの種類は他にもあるのでしょうか?Markerは、マップ上の特定の座標にコンテンツを表示するために使用されます。
吹き出しの形は見覚えがあるかもしれません。
マーカーは、マップアプリや、App Storeで見つかるさまざまなアプリを含むプラットフォーム全体で使用されています。
マーカーと同様に、アノテーションは特定の座標にコンテンツを表示するために使用されます。
Marker のバルーンの代わりに、Annotation は SwiftUI View を表示します。
コンテンツビルダーはオーバーレイコンテンツを表示するためにも使うことができます。
これらについてはもう少し後で詳しく説明する。
今のところ、本当に知っておく必要があるのは、マップにあらゆる種類のコンテンツを追加するためにコンテンツビルダークロージャを使うことができるということです。
私は駐車場のためにカスタムSwiftUIビューを表示したいので、それをマークするためにアノテーションを使用します。
ここでは、ZStackを使っていくつかの図形と画像を合成している。
このSwiftUIのビューは駐車座標の右を中心として地図上に表示されます。
代わりに座標の上にビューを配置したい場合、アノテーションのアンカーパラメータを使用することができます。
「bottom」のアンカー値を指定すると、ビューの下部がアノテーションの座標の右側に配置されます。
アプリはウォーキングツアーを開始する場所を示しています。
MapContentBuilderを使って地図上にアノテーションのコンテンツを表示しました。
次に、このアプリで地図を見ながら場所の感覚を伝えたいです。
Give the user a sense of place(ユーザーに場所を感じさせる)
mapStyleを使って、リアルな地形標高を表示できるようにします。mapStyleを使って衛星写真やフライオーバー画像を表示する方法も学びます。
mapStyleモディファイアを使ってスタイルを設定できます。
ここでは標準の地図スタイルを指定しています。 デフォルトでは、物理的な紙の地図のような平坦な表示です。
ラグーンには橋が架かっているようで、片側から反対側へ歩いて行くことができる。
この平らな地図は想像をかき立てる。
リアルな高台の地形を有効にして、マップに別の次元を与えてみる。
リアルな標高を有効にすることで、マップがリアルに表現されます。
今ラグーンを見ると、夏になると周遊するスワンボートに乗って、橋の下を通過するボートを想像することができる。
イメージマップスタイル(.imagery)を使うのも、ユーザーに場所の感覚を提供する良い方法です。
イメージマップスタイルは、衛星写真や上空写真を使ってレンダリングされた地図を表示します。
ハイブリッドマップスタイル(.hybrid)は画像と道路とラベルを組み合わせたものです。
おさらいすると、標準のmapStyleを使ってリアルな標高を有効にし、他のマップスタイルの使い方も紹介しました。
次に、アプリに行きたい場所の検索をしてもらいましょう。
Search for places(場所を探す)
子供たちも一緒にボストンを歩くので、子供たちにとっても楽しい朝にしたい。
大人たちは歴史に触れ、子供たちはブランコや滑り台、モンキーバーを楽しむことができる!遊び場(playground)を検索するボタンと、ビーチ(beach)を検索するボタンも追加しよう。
アプリは検索結果ごとにマーカーを追加します。マーカーについてもう少し学び、検索結果の邪魔にならないようにマップの上に独自のUIを表示する方法も学びます。
さきほどBeantownButtons Viewを少し作ってみた。
ボタンをタップすると、遊び場かビーチのどちらかの簡単なクエリで検索機能が呼び出される。
検索機能では、MKLocalSearchを使ってボストンコモンの駐車場付近の場所を検索し、バインディング(Binding)を使って結果を書き込みます。
アプリのメインのContentViewに戻って、Stateを追加して検索結果を記録する(searchResults)。
BeantownButtons UIが検索を実行すると、バインディングを使ってこのステートに結果を書き戻します。
画面下のマップの上にボタンを追加します。
safeAreaInsetを使うことで、アプリのUIが追加するコンテンツや、Apple Mapsのロゴやリーガルリンクのような地図上に表示されるシステム提供のコントロールを隠さないようにします。
次に、コンテンツビルダーを使って検索結果のマーカーを追加します。
ForEachを使って検索結果ごとにマーカーを追加しています。
このボタンを試してみる。遊び場を探そう
見て 遊び場!地図が自動的に枠を作り、ズームアウトして、すべての遊び場が見えるようになっている。
ビーチはどう?検索結果はMKMapItemsで、MKLocalSearchのようなMapKit APIが場所を表すのに使うタイプです。
ここでは、Markerのマップアイテム・イニシャライザを使っている。
この方法で作成したマーカーは、マップアイテムの名前をタイトルに使い、マップアイテムの情報を使って、その場所を表すアイコンと色合いを表示します。
これらの検索結果のほとんどは、水色のビーチパラソルマーカーとして表示されます。
地図アイテムを扱うとき、Markerの自動コンテンツおよびスタイルサポートはとても便利です。
マップアイテムを使用していない場合でも、マーカーの表示はコントロールできます。
デフォルトでは、Markerのバルーンにマップピンのアイコンが表示されます。
イメージアセットまたはシステムイメージを使って、独自のアイコンを提供することができます。
モノグラムを使って3文字までのテキストを表示することもできます。
マーカーの色はtintモディファイアを使って変更できます。
まとめると、safeAreaInset を使って、検索結果のマーカーが見えなくならないようにしながら、いくつかのボタンをマップの上に表示しています。
Display a place or region(場所または地域を表示する)
次に、地図が表示する内容をアプリがコントロールできるようにする。
私は地図にコンテンツを追加してきた。
そのたびに、マップは自動的に私のために私のコンテンツをフレーミングしてくれた。
必要なときにこの便利な動作を有効にする方法を紹介しよう。
また、ボストン地域のノースショアの海岸線のように、まったく別のものを表示する方法も紹介しよう。
今はビーチを見ています。
ボストン・コモンの駐車場付近の地図が自動的に表示されなくなった。
ボストン・コモンの駐車場近くの検索結果が自動的に表示されなくなりました。
ユーザーがマップを操作した後に検索結果を表示するには、マップのカメラ位置の状態を再設定して、マップがマーカーを囲むようにする必要があります。
そこで、位置を追跡するステートを追加する。
マップに追加したコンテンツを枠で囲むデフォルトの自動位置を使うことにする。
そしてバインディングをMapのイニシャライザーに渡す。
検索結果がいつ更新されるかは、onChangeモディファイアを使って確認する。
検索結果が更新されたら、カメラ位置を自動に戻し、検索結果が表示されるようにする。
試してみよう。
ビーチを検索し、その結果を見てから、運動場を検索する前にパンする。
クールだ!これで、検索を実行すると、ロードアイランドまでパンしていたとしても、結果がすべて表示されるようになった。
もうひとつ、このポジションを使ってやりたいことがある。
ボストンで楽しい午前を過ごした後、私の家族はビーチで午後を過ごすために北へ車を走らせる予定だ。
アプリで北岸の海岸線を簡単に見られるようにして、これから行く場所の雰囲気を掴めるようにしてほしい。
そのために位置の状態を使う。
市とノースショアの座標地域を追加する。
BeantownButtonsのUIに切り替えて、ポジション・ステートのバインディングを追加する。
いくつかのボタンを追加して、それぞれカメラの位置をリージョンに設定する。
都市ボタンを押すと、地図にボストンが表示される。
Wavesボタンを押すと、地図に北岸の海岸線が表示される。
ContentViewに戻って、ボタンのUIに位置バインディングを渡す。
試してみよう!「waves」ボタンをタップすると、地図の位置が更新され、北岸の海岸線地域が表示された。
「都市」ボタンをタップすると、ボストンの表示に更新される。
Control what the map displays(地図の表示内容をコントロールする)
舞台裏では、マップが表示するものは最終的にMapCameraによってコントロールされる。
Position the camera(カメラの位置)
カメラはある距離から地上の座標を見、カメラの向きによって地図に何が見えるかが決まる。
私が作っているアプリでは、カメラ自体を作ったり設定したりする必要はない。
その代わりに、MapCameraPositionを使って何が見えるかを指定するだけです。
MapKitは私のためにカメラの面倒を見てくれる。
このアプリは、検索結果のようなコンテンツをフレームで囲むために自動カメラ位置を使用します。
ボストンとノースショアを表示するには、地域の位置を使う。
カメラ位置を指定して、他のものをフレーム化することもできる。
矩形位置は、regionの使い方と同じように、エリアを表示するのに使います。
これは、座標領域の代わりに、その領域を表すためにマップの矩形を使うだけです。
アイテム、カメラ、ユーザー位置のカメラ位置を詳しく見てみましょう。
MKMapItemを使用すると、特定の場所を表示することができます。
これはあらゆる種類の地図アイテムに使えます。
マップのアイテムがケープコッド湾を表している場合、MapKitはそれが収まるように自動的にズームアウトします。
ノースエンドのある公園を見せようとすると、カメラがズームインして周囲を映し出し、場所の感覚を伝えます。
また、思い通りに設定したMapCameraを提供することもできます。
ピッチアングルのMapCameraを使えば、3Dパースペクティブを表現するのに最適です。
あるいは、チャールズ川沿いを歩いているユーザーの位置をカメラで追いかけたいかもしれない。
位置の承認が得られていない場合や、デバイスが位置の修正を試みている場合など、ユーザの位置が不明な場合に使用されるフォールバック位置を指定できます。
カメラ位置の状態へのバインディングを提供すると、カメラ位置が変更されたときに MapKitがそれを更新します。
ユーザーロケーションのカメラ位置です。
followsUserLocationプロパティはtrueです。
ユーザーがパンして離れると、カメラはユーザーの位置を追跡しなくなります。
ユーザがマップを操作すると、カメラの位置状態はpositionByUserになります。
アプリがカメラの位置状態をuserLocationに戻すと、カメラはユーザーの位置に追従します。
アプリがカメラ位置の状態を設定するとき、それはpositionedByUserではありません。
どのタイプのカメラ・ポジションを指定しても、ユーザはマップを操作してカメラを配置できます。
Search in the visible region(可視領域での検索)
よし!アプリが地図上の表示をコントロールできるようになりました。
自動カメラ位置を使って、ユーザーが地図とインタラクションした後でも検索結果が表示されるようにした。
ボストンとノースショアを表示するために、地域のカメラ位置を使いました。
次は、ボストン・コモンの近くだけを検索するのではなく、自分が行ってみたい地域に地図をパンして、そこを代わりに検索したいと思います。
カメラが変わったときに見える地域を取得する方法をお見せします。
マップに表示されている領域を追跡するためにステートを追加する。
onMapCameraChangeモディファイアを追加して、更新コンテキストから可視領域を取得して、自分のステートに格納する。
デフォルトでは、onMapCameraChangeに指定されたクロージャは、ユーザがマップとのインタラクションを終了したときに呼び出される。
ユーザがマップとインタラクトしている間にクロージャを呼び出すには、frequencyパラメータを渡すことで継続的な更新を要求できる。
ここで使っているリージョンプロパティに加えて、コンテキストは可視マップ矩形とマップカメラ自体のプロパティも持っている。
ニーズによっては、これらを使うこともできる。
BeantownButtonsを更新して、ユーザから見える範囲内を検索するようにしよう。
ボタンにvisibleRegionを追加します。
そして、それを検索リクエストに使う。
ContentViewで、visibleRegionをボタンUIに渡す。
ノースショアのビーチを検索してみよう!ノースショアがある
ビーチを見せてくれ!いいね!ロードアイランドはどう?
いいね!これでロードアイランドのビーチも検索できる!これは
onMapCameraChangeは、見えるものが変わったときに知らせてくれる。
次に、どのビーチに行くかをもう少し簡単に選べるようにしてほしい。
確かにたくさんの中から選ぶことができる。
手始めに、検索結果を選択する機能を追加しよう。
今は、検索結果のマーカーをタップしても何も起こらない。
選択状態がないので、マーカーは選択できない。
選択できるようにするには、Mapに選択バインディングを追加します。
では、結果をタップするとどうなるか見てみよう。
バルーンは選択されていることを示すためにアニメーションします!選択タイプとしてMKMapItemを使っているので、マップアイテムを表す各マーカーが選択可能になりました。
パーキングスポットのアノテーションはマップアイテムを表していないので、選択できません。
必ずしも同じタイプの ID を持たないマーカーやアノテーションの選択をサポートしたい場合は、単純にタグを付けることができます。
Display useful search-result information(有用な検索結果情報の表示)
これは、ピッカーやリストで選択を管理するときと同じように機能する。
ここでは、selectedTagの状態はIntです。
各マーカーにはIntのタグが付けられているため、バインディングによって両方のマーカーで選択が有効になります。
タグを使用して選択を有効にする場合、選択状態にはhashableに準拠した任意の型を使用できます。
まとめると、MapにMKMapItemの選択バインディングを追加して、検索結果のマーカーを選択できるようにしました。
次に、アプリは選択された検索結果に関する追加情報を表示する必要があります。
そして、ビーチの名前とドライブ時間も追加する。
今日、BeantownButtons Viewを書くときに、ItemInfoViewも少し書いた。
タイトル、推定所要時間、そしてLook Aroundプレビュー。
Look Around Previewは、選択したビーチがどのように見えるかを表示してくれる。
プレビューはルックアラウンドシーンを表示します。
MKLAookAroundSceneRequest を使用して、指定されたマップアイテムのシーンを取得できます。
シーンは、ビューが表示されるときに取得され、選択された検索結果が変更されるたびに再度取得されます。
最後に、DateComponentsFormatterを使用して、MKRouteの予想移動時間を表示用にフォーマットするプロパティがあります。
ContentViewに戻って、このItemInfoViewを追加する。
まず最初に、駐車場から選択した検索結果までのルートを取得する。
ルートを追跡するためのステートを追加する。
そして、MKDirectionsを使ってルートを取得する関数を追加します。
そしてステートを設定する。
もうひとつonChangeモディファイアを追加して、選択範囲が変わったときに関数を呼び出すようにする。
検索結果が選択されると、アプリはアイテム情報ビューを表示します。
ついでに、検索結果のマーカーのタイトルを非表示にして、地図の見た目を少しすっきりさせよう。
ItemInfoViewには、代わりに選択された場所の名前が表示されます。
なるほど!何ができたか見てみよう。
そういえば、この近くに遊び場があったような…。
なかなかいい感じですね!
Show overlay content(オーバーレイ・コンテンツを表示する)
こっちには何があるんだろう?いいね!遊び場付きのビーチがあるんだろ?私たち家族には、かなり厳しいわね。
ボストンコモンから30分だ
いい場所だ
まとめとして、マーカーを選択したときに表示される、周囲を見渡すプレビューを追加した。
MKRouteからの推定所要時間と合わせて、ビーチを選ぶのに大いに役立つだろう。
次に、移動時間を表示するルートはすでにあるので、それを使ってボストン・コモンから選択した検索結果までのドライブルートを表示しよう。
ルートを表示するためにMapPolylineオーバーレイを追加します。
ルートが利用可能になったら、MapPolyline を追加し、青で描画します。
地図で見てみましょう。
なかなかいいドライブのようだ!
MKRouteでMapPolylineを使うのはとても簡単だった。
また、MapPolylineを使って独自の位置データを表示することもできます。
StrokeStyleを使用すると、ダッシュやグラデーションなど、かなり派手なものを表示できます。
エリアを強調したい場合は、MapPolygonかMapCircleを使うとよい。
ここに、いくつかの公園を示す2つのポリゴンがある。
ここに同じ公園を示す2つの円がある。
それぞれの円にオーバーレイ・レベルが指定されていることがわかるだろう。
ピンクの円は、デフォルトのオーバーレイ・レベルである “above roads “を使用しており、地図のラベルが円の上に表示されている。
シアンの円は、ラベルの上に表示されている。
アプリは本当に形になってきた。
ビーチまでのドライブルートを表示するためにMapPolylineを追加し、他にも使えるオーバーレイタイプをいくつか紹介しました。
次は、自分がどこにいるのかを簡単に把握できるアプリにしたい。
ボストンに着いて歩き始めたら、ちょっと道に迷うかもしれない。
自分がどこにいるかを示すためにUserAnnotationをコンテンツとして追加し、自分を見つけるためにMapUserLocationButtonを追加します。
他にもいくつかのタイプのマップ・コントロールがあることを知るだろう。
自分を探そうとするとき、私はたいてい地図上の小さな青い点を探すことから始める。
UserAnnotationをマップ・コンテンツに追加したので、自分の位置がマップ上に表示されるようになった。
ここはどこ?ここだ!今までチェックしてきた遊び場やビーチからはかなり離れているようだ。
アップルパークに行くには、かなりズームアウトしてパンしなければならなかった。
MapUserLocationButtonを使えば、それが簡単になることは間違いない。
これでボタンをタップして自分の位置を表示できる。
マップカメラは私が移動すると追いかけてくる。
MapCompassとMapScaleViewも追加した。
デフォルトのmapControlsコンフィギュレーションでは、地図を回転させるとコンパスが表示され、拡大・縮小すると縮尺インジケーターが表示される。
このアプリでもこれらのデフォルト・コントロールが欲しいので、ユーザーの位置情報ボタンに加えて指定しました。
これらはすべてmapControlsモディファイアを使って追加したので、マップは自動的にデフォルトの位置に表示されます。
これには、macOSにあるMapZoomStepperとMapPitchSliderを含む、全てのプラットフォームのマップコントロールが含まれます。
これらのコントロールを自分で配置したい場合は、独自のUIで表示することができる。
Mapコントロールは単なるビューなので、mapControlsモディファイアを使う代わりに、他のビューと同じように追加することができます。
この場合、mapScopeモディファイアを使用して、コントロールを特定のMapスコープに関連付ける必要があります。
プレゼンテーションもそろそろ終わりなので、今日学んだことをまとめましょう。
SwiftUI のためのMapKitは、あなたのアプリにマップを統合するための信じられないほど強力で使いやすいAPIです。
マーカー、アノテーション、オーバーレイを使ってマップ上にコンテンツを表示することができます。
Map CameraとMap Controlsを使えば、ニーズに合わせて地図をカスタマイズできます。
最後に、MapStyleとLook Aroundは、ユーザーにリアルな場所の感覚を与えます。
これらはSwiftUIのためのMapKitの機能のほんの一部です。
そしてもちろん、これはSwiftUIなので、あなたのマップはすべてのプラットフォームで素晴らしく見えるでしょう!最後に少し考えをまとめます。
私たちはオートコンプリートと道順をサポートするためにApple Maps Server APIを拡張しました。
Apple Maps Server APIsの使い方については、昨年のWWDCセッション「Meet Apple Maps Server APIs」をご覧ください。