mapkit について
SwiftUIまたはUIKitでAppleプラットフォーム向けアプリを開発する人のためのMapKitリファレンススキルです。地図、位置情報、アノテーション、ルート検索、場所検索、オーバーレイを扱い、詳しいAPI資料への参照も示します。
主な機能
- 01SwiftUIとUIKitのサンプル
- 02地図とユーザー位置の表示
- 03アノテーションとクラスタリング
- 04ルート計算と場所検索
- 05地図スタイルとオーバーレイの設定
活用シーン
- 01アプリにインタラクティブな地図を表示
- 02場所のマーカーや多数のアノテーションを管理
- 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
主な考慮事項
- プライバシー:Info.plist に位置情報の使用目的を必ず追加してください
- マップ機能:経路案内には、Xcode の「Signing & Capabilities」で有効にしてください
- iOS のバージョン:SwiftUI の Map API は iOS 17 で大幅に改善されました。古いターゲットには UIKit を使用してください
- クラスタリング:自動クラスタリングを有効にするには、注釈ビューに
clusteringIdentifierを設定します(UIKit) - パフォーマンス:注釈が多数(1000 件以上)ある場合は、クラスタリングまたはカスタムタイルオーバーレイを検討してください
リファレンス全文を参照する場面
以下を確認するには、references/MapKit.md を検索してください:
- API のシグネチャとパラメーターの全容
- LookAround の実装の詳細
- MKMapItem と場所の詳細
- GeoJSON のデコード
- 屋内マッピング(IMDF)
- POI のカテゴリとフィルタリング
- スナップショットの生成
- すべての MKMapViewDelegate メソッド
コメント
Markdown 対応