WWDC Quick Look 💓 By SwiftGGTeam
Meet MapKit for SwiftUI

Meet MapKit for SwiftUI

元の動画を見る

ハイライト

MapKit エンジニアの Jeff が 30 分未満でボストン旅行計画アプリをゼロから構築し、MapContentBuilder クロージャと宣言型 API による SwiftUI 版 MapKit が地図統合のハードルをいかに下げるかを示しました。

主要内容

SwiftUI で地図を使うのは以前は面倒でした。UIKit 時代の MKMapView はブリッジが必要で、カスタム注釈やオーバーレイには大量のデリゲートコードが必要で、カメラ制御は領域とズームレベルを手動計算する必要がありました。

01:42)Jeff のデモは空の SwiftUI プロジェクトから始まります。MapKit をインポートし、Map() を 1 行追加するだけで、インタラクティブな地図が表示されます。デリゲートも設定もブリッジも不要です。

02:41)注釈コンテンツは MapContentBuilder クロージャで追加し、List に View を追加する構文とほぼ同じです。Marker は標準のバルーンアイコンを表示し、Annotation には任意の SwiftUI View を配置できます。地図は自動的に視野を調整して全コンテンツを枠内に収めます。これがデフォルト動作で、手動計算は不要です。

04:36)地図スタイルは mapStyle 修飾子で切り替えます。標準地図はデフォルトで平面表示ですが、.standard(elevation: .realistic) を追加すると地形に実際の標高が付き、橋や湖に立体感が生まれます。衛星図やハイブリッドモードにも切り替え可能です。

09:10)カメラ制御は MapCameraPosition 状態で実現します。automatic は地図がコンテンツを自動的に枠内に収め、region は指定領域を表示、item は特定の場所にフォーカス、userLocation はユーザー位置を追従します。ユーザーが手動で地図をドラッグすると、状態は positionedByUser になり、アプリは再配置が必要かどうかを判断できます。

17:33)選択インタラクションは Map に selection バインディングを追加するだけです。Marker は自動的に選択アニメーションを表示します。onChange と組み合わせれば、選択時にルートを取得し、Look Around 街景プレビューを表示できます。

21:54)オーバーレイもコンテンツビルダーで追加します。MapPolyline はルートを表示し、MapPolygonMapCircle は領域をマークします。各オーバーレイは aboveRoads または aboveLabels レベルを指定し、地図ラベルとの重なり順を制御できます。

23:47)地図コントロールには MapUserLocationButtonMapCompassMapScaleView があり、mapControls 修飾子で自動レイアウトされます。通常の View として手動配置も可能で、その場合は mapScope で特定の地図インスタンスに関連付けます。

詳細

最小限の地図

import MapKit
import SwiftUI

struct ContentView: View {
    var body: some View {
        Map()
    }
}

キーポイント:

  • 1 行のコードでドラッグ、ズーム、回転に対応するインタラクティブな地図を取得
  • ユーザーの位置情報認可とコンパス方向を自動処理

注釈とカスタムビューの追加

struct ContentView: View {
    var body: some View {
        Map {
// 標準 Marker、バルーンアイコン
            Marker("Parking", coordinate: .parkingGarage)
            
// カスタム Annotation、任意の SwiftUI View を配置
            Annotation("Custom Spot", coordinate: .customSpot) {
                ZStack {
                    Circle().fill(.blue)
                    Image(systemName: "car.fill")
                }
            }
        }
    }
}

キーポイント:

  • Marker はシンプルな地点マーカーに適し、システムがアイコンと色を処理
  • Annotation のクロージャ内に完全な SwiftUI ビュー階層を記述可能
  • anchor: .bottom でビューの下端を座標に合わせ、中心ではなく下端を基準に配置

検索結果に基づく動的注釈

struct ContentView: View {
    @State private var searchResults: [MKMapItem] = []
    @State private var position: MapCameraPosition = .automatic
    
    var body: some View {
        Map(position: $position) {
            ForEach(searchResults, id: \.self) { item in
                Marker(item: item)
            }
        }
        .onChange(of: searchResults) {
            position = .automatic
        }
    }
    
    func search(for query: String) {
        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = query
        request.region = visibleRegion
        
        MKLocalSearch(request: request).start { response, _ in
            searchResults = response?.mapItems ?? []
        }
    }
}

キーポイント:

  • Marker(item:) は MKMapItem の名前、アイコン、色調を自動使用
  • ビーチを検索すると傘アイコン、遊び場を検索すると滑り台アイコンが表示
  • position = .automatic で地図が全結果を表示するよう自動ズーム
  • ユーザーがドラッグ後に検索する場合、手動で position をリセットして再枠付けが必要

カメラ位置の状態

@State private var position: MapCameraPosition = .region(.boston)

// 指定した領域を表示
position = .region(MKCoordinateRegion(
    center: CLLocationCoordinate2D(latitude: 42.36, longitude: -71.06),
    span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
))

// ユーザー位置を追従し、未許可の場合は指定領域にフォールバック
position = .userLocation(fallback: .region(.boston))

// ある場所を表示し、適切なズームレベルを自動計算
position = .item(MKMapItem(placemark: MKPlacemark(coordinate: .beach)))

キーポイント:

  • MapCameraPosition は値型で、バインディングにより Map と双方向同期
  • ユーザー操作後は状態が positionedByUser になり、アプリが検出して応答可能
  • onMapCameraChange で現在の可視領域を取得し、連続更新または操作終了時のみのトリガーに対応

ルートオーバーレイ

Map {
    if let route {
        MapPolyline(route.polyline)
            .stroke(.blue, lineWidth: 5)
    }
}

// ルートを取得
func getRoute(from source: MKMapItem, to destination: MKMapItem) {
    let request = MKDirections.Request()
    request.source = source
    request.destination = destination
    
    MKDirections(request: request).calculate { response, _ in
        route = response?.routes.first
    }
}

キーポイント:

  • MapPolylineMKPolyline を直接受け取り、座標変換は不要
  • StrokeStyle は破線、グラデーションなどに対応
  • MapPolygonMapCircle は領域をマーク。aboveRoads / aboveLabels で道路・ラベルとの重なり順を制御。aboveRoads は地図ラベルがオーバーレイの上に、aboveLabels はオーバーレイがラベルの上に表示

地図コントロール

Map {
// コンテンツ
}
.mapControls {
    MapUserLocationButton()
    MapCompass()
    MapScaleView()
}

キーポイント:

  • mapControls はコントロールを適切な位置に自動レイアウト
  • 手動配置時は mapScope で関連付け:.mapScope(mapScope)
  • macOS には追加で MapZoomStepperMapPitchSlider が提供

重要ポイント

  • ローカルライフスタイルアプリにインタラクティブな地図を追加する:レストラン、ホテル、イベント系アプリは MapKit for SwiftUI で「近くのおすすめ」機能を素早く実装できます。Marker はカテゴリアイコンを自動表示し、Look Around で街景プレビューを提供。ユーザーはアプリを離れずに場所の適切さを判断できます。

  • 地図でデータ可視化を行う:配送ルート、運動コース、旅行の足跡など位置情報関連データがある場合、MapPolyline とカスタム Annotation で地図上に直接表示すればリストより直感的です。onMapCameraChange と組み合わせれば、現在の視野内のデータポイントのみ読み込めます。

  • 旅行計画ツールを作る:セッションのボストン行程計画のアプローチを参考に、検索で観光地を見つけ、Marker でマークし、ルートで接続し、Look Around でプレビュー。SwiftUI の宣言型構文により、この多段階インタラクションの実装コストが大幅に下がります。

関連セッション

コメント

GitHub Issues · utterances