mcpField
M
mapkit

mapkit

github.com/ios-agent開発1250 回インストール35 週間前に更新
リポジトリ

mapkit について

SwiftUIまたはUIKitでAppleプラットフォーム向けアプリを開発する人のためのMapKitリファレンススキルです。地図、位置情報、アノテーション、ルート検索、場所検索、オーバーレイを扱い、詳しいAPI資料への参照も示します。

主な機能

  1. 01SwiftUIとUIKitのサンプル
  2. 02地図とユーザー位置の表示
  3. 03アノテーションとクラスタリング
  4. 04ルート計算と場所検索
  5. 05地図スタイルとオーバーレイの設定

活用シーン

  1. 01アプリにインタラクティブな地図を表示
  2. 02場所のマーカーや多数のアノテーションを管理
  3. 03ルート検索や場所検索機能を追加

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ios-agent/iosagent.dev --skill mapkit

インストール後、Claude Code で/mapkitを入力して読み込まれたか確認します。

同梱ファイルreferences/MapKit.md

詳細

Apple MapKit との統合

SwiftUI または UIKit を使用して、開発者が iOS/macOS/visionOS アプリに MapKit を統合できるよう支援します。

クイックリファレンス

  • API ドキュメント全文:MapKit API の詳細については、references/MapKit.md を参照してください
  • 新規プロジェクトでは SwiftUI を標準とし(iOS 17 以降)、古いターゲットや特定のニーズには UIKit を提案してください

よくあるワークフロー

1. 基本的なマップを表示する

SwiftUI(iOS 17 以降)

import MapKit
import SwiftUI

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

初期位置を指定した SwiftUI

struct ContentView: View {
    @State private var position: MapCameraPosition = .region(
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
            span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
        )
    )
    
    var body: some View {
        Map(position: $position)
    }
}

UIKit

import MapKit
import UIKit

class MapViewController: UIViewController {
    private let mapView = MKMapView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        mapView.frame = view.bounds
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(mapView)
        
        let region = MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
            span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
        )
        mapView.setRegion(region, animated: false)
    }
}
2. ユーザーの位置を表示する

必須:Info.plist に NSLocationWhenInUseUsageDescription を追加します

SwiftUI(iOS 17 以降)

import CoreLocation
import MapKit
import SwiftUI

struct ContentView: View {
    @State private var position: MapCameraPosition = .userLocation(fallback: .automatic)
    
    var body: some View {
        Map(position: $position) {
            UserAnnotation()
        }
        .mapControls {
            MapUserLocationButton()
            MapCompass()
        }
    }
}

UIKit

class MapViewController: UIViewController, CLLocationManagerDelegate {
    private let mapView = MKMapView()
    private let locationManager = CLLocationManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupMapView()
        locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if manager.authorizationStatus == .authorizedWhenInUse {
            mapView.showsUserLocation = true
            mapView.userTrackingMode = .follow
        }
    }
}
3. 注釈/マーカーを追加する

SwiftUI(iOS 17 以降)

struct Place: Identifiable {
    let id = UUID()
    let name: String
    let coordinate: CLLocationCoordinate2D
}

struct ContentView: View {
    let places = [
        Place(name: "San Francisco", coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)),
        Place(name: "Oakland", coordinate: CLLocationCoordinate2D(latitude: 37.8044, longitude: -122.2712))
    ]
    
    var body: some View {
        Map {
            ForEach(places) { place in
                Marker(place.name, coordinate: place.coordinate)
            }
        }
    }
}

カスタム注釈コンテンツ

Map {
    ForEach(places) { place in
        Annotation(place.name, coordinate: place.coordinate) {
            VStack {
                Image(systemName: "mappin.circle.fill")
                    .font(.title)
                    .foregroundStyle(.red)
                Text(place.name)
                    .font(.caption)
            }
        }
    }
}

UIKit

class PlaceAnnotation: NSObject, MKAnnotation {
    let title: String?
    let coordinate: CLLocationCoordinate2D
    
    init(title: String, coordinate: CLLocationCoordinate2D) {
        self.title = title
        self.coordinate = coordinate
    }
}

// In view controller:
let annotation = PlaceAnnotation(
    title: "San Francisco",
    coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)
)
mapView.addAnnotation(annotation)
4. マーカー/注釈のクラスタリング

SwiftUI(iOS 17 以降) — .annotationTitles(.hidden) を指定した MapContentBuilder を使ってクラスタリング動作を実現するか、UIKit で MKClusterAnnotation を使用します。

クラスタリングを使用する UIKit

class ClusterableAnnotation: NSObject, MKAnnotation {
    let coordinate: CLLocationCoordinate2D
    let title: String?
    
    init(coordinate: CLLocationCoordinate2D, title: String?) {
        self.coordinate = coordinate
        self.title = title
    }
}

class MapViewController: UIViewController, MKMapViewDelegate {
    private let mapView = MKMapView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        mapView.delegate = self
        mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
        mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
    }
    
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard !(annotation is MKUserLocation) else { return nil }
        
        if let cluster = annotation as? MKClusterAnnotation {
            let view = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier, for: annotation) as! MKMarkerAnnotationView
            view.markerTintColor = .blue
            view.glyphText = "\(cluster.memberAnnotations.count)"
            return view
        }
        
        let view = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier, for: annotation) as! MKMarkerAnnotationView
        view.clusteringIdentifier = "places" // Enable clustering
        view.markerTintColor = .red
        return view
    }
}
5. 経路案内とルート検索

SwiftUI(iOS 17 以降)

struct DirectionsView: View {
    @State private var route: MKRoute?
    @State private var position: MapCameraPosition = .automatic
    
    let start = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)
    let end = CLLocationCoordinate2D(latitude: 37.8044, longitude: -122.2712)
    
    var body: some View {
        Map(position: $position) {
            Marker("Start", coordinate: start)
            Marker("End", coordinate: end)
            if let route {
                MapPolyline(route.polyline)
                    .stroke(.blue, lineWidth: 5)
            }
        }
        .task {
            await calculateRoute()
        }
    }
    
    func calculateRoute() async {
        let request = MKDirections.Request()
        request.source = MKMapItem(placemark: MKPlacemark(coordinate: start))
        request.destination = MKMapItem(placemark: MKPlacemark(coordinate: end))
        request.transportType = .automobile
        
        let directions = MKDirections(request: request)
        if let response = try? await directions.calculate() {
            route = response.routes.first
        }
    }
}

UIKit

func calculateAndDisplayRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) {
    let request = MKDirections.Request()
    request.source = MKMapItem(placemark: MKPlacemark(coordinate: source))
    request.destination = MKMapItem(placemark: MKPlacemark(coordinate: destination))
    request.transportType = .automobile
    
    let directions = MKDirections(request: request)
    directions.calculate { [weak self] response, error in
        guard let route = response?.routes.first else { return }
        self?.mapView.addOverlay(route.polyline)
        self?.mapView.setVisibleMapRect(route.polyline.boundingMapRect, edgePadding: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50), animated: true)
    }
}

// MKMapViewDelegate
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if let polyline = overlay as? MKPolyline {
        let renderer = MKPolylineRenderer(polyline: polyline)
        renderer.strokeColor = .systemBlue
        renderer.lineWidth = 5
        return renderer
    }
    return MKOverlayRenderer(overlay: overlay)
}
6. ローカル検索(場所を検索)
func searchForPlaces(query: String, region: MKCoordinateRegion) async -> [MKMapItem] {
    let request = MKLocalSearch.Request()
    request.naturalLanguageQuery = query
    request.region = region
    
    let search = MKLocalSearch(request: request)
    if let response = try? await search.start() {
        return response.mapItems
    }
    return []
}

// Usage
let results = await searchForPlaces(query: "coffee", region: mapView.region)
for item in results {
    print("\(item.name ?? "") - \(item.placemark.coordinate)")
}

検索候補(オートコンプリート)

class SearchCompleter: NSObject, ObservableObject, MKLocalSearchCompleterDelegate {
    @Published var results: [MKLocalSearchCompletion] = []
    private let completer = MKLocalSearchCompleter()
    
    override init() {
        super.init()
        completer.delegate = self
        completer.resultTypes = [.address, .pointOfInterest]
    }
    
    func search(query: String) {
        completer.queryFragment = query
    }
    
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        results = completer.results
    }
}
7. マップオーバーレイ(ポリライン、ポリゴン、円)

SwiftUI(iOS 17 以降)

Map {
    // Polyline
    MapPolyline(coordinates: [
        CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
        CLLocationCoordinate2D(latitude: 37.8044, longitude: -122.2712)
    ])
    .stroke(.blue, lineWidth: 3)
    
    // Circle
    MapCircle(center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), radius: 1000)
        .foregroundStyle(.blue.opacity(0.3))
        .stroke(.blue, lineWidth: 2)
    
    // Polygon
    MapPolygon(coordinates: [
        CLLocationCoordinate2D(latitude: 37.77, longitude: -122.42),
        CLLocationCoordinate2D(latitude: 37.78, longitude: -122.40),
        CLLocationCoordinate2D(latitude: 37.76, longitude: -122.40)
    ])
    .foregroundStyle(.green.opacity(0.3))
    .stroke(.green, lineWidth: 2)
}
8. マップの設定

SwiftUI

Map {
    // content
}
.mapStyle(.standard) // .imagery, .hybrid, .standard(elevation: .realistic)
.mapControls {
    MapUserLocationButton()
    MapCompass()
    MapScaleView()
    MapPitchToggle()
}

UIKit

mapView.mapType = .standard // .satellite, .hybrid, .satelliteFlyover, .hybridFlyover
mapView.showsCompass = true
mapView.showsScale = true
mapView.isZoomEnabled = true
mapView.isScrollEnabled = true
mapView.isPitchEnabled = true
mapView.isRotateEnabled = true

主な考慮事項

  1. プライバシー:Info.plist に位置情報の使用目的を必ず追加してください
  2. マップ機能:経路案内には、Xcode の「Signing & Capabilities」で有効にしてください
  3. iOS のバージョン:SwiftUI の Map API は iOS 17 で大幅に改善されました。古いターゲットには UIKit を使用してください
  4. クラスタリング:自動クラスタリングを有効にするには、注釈ビューに clusteringIdentifier を設定します(UIKit)
  5. パフォーマンス:注釈が多数(1000 件以上)ある場合は、クラスタリングまたはカスタムタイルオーバーレイを検討してください

リファレンス全文を参照する場面

以下を確認するには、references/MapKit.md を検索してください:

  • API のシグネチャとパラメーターの全容
  • LookAround の実装の詳細
  • MKMapItem と場所の詳細
  • GeoJSON のデコード
  • 屋内マッピング(IMDF)
  • POI のカテゴリとフィルタリング
  • スナップショットの生成
  • すべての MKMapViewDelegate メソッド

コメント