【Swift】MapKit でストリートビューを実装する
初めに
今回は SwiftUI と MapKit でストリートビュー(LookAroundViewer)を実装してみたいと思います。
なお、今回は visionOS 上で動作させていますが、のページに飛ぶと以下に対応していることがわかり、iOSでも使用できるかと思います。
- iOS 17.0+
- iPadOS 17.0+
- visionOS 1.0+
記事の対象者
- Swift, SwiftUI学習者
- アプリに LookAroundViewer を導入したい方
目的
今回は上記の通り MapKit で LookAroundViewer
を実装することを目的とします。
最終的には以下のようにマップからポイントを指定して、その場所の LookAroundViewer
を確認できるような実装を行いたいと思います。
実装
シンプルな実装
初めに LookAroundViewer
を実装した動くコードを以下に提示します。
ContentView
と入れ替えればそのまま動作するかと思います。
import SwiftUI
import MapKit
struct LookAroundView: View {
@State private var position: MapCameraPosition = .region(.init(
center: .init(latitude: 34.687234, longitude: 135.525842),
span: .init(latitudeDelta: 0.005, longitudeDelta: 0.005)
))
@State private var lookAroundScene: MKLookAroundScene?
@State private var isLookAroundPreviewShown = false
var body: some View {
MapReader { mapProxy in
Map(position: $position)
.onTapGesture { location in
guard let selectedLocation = mapProxy.convert(location, from: .local) else { return }
requestLookAroundScene(selectedLocation: selectedLocation)
isLookAroundPreviewShown = true
}
}
.lookAroundViewer(
isPresented: $isLookAroundPreviewShown,
scene: $lookAroundScene
)
}
func requestLookAroundScene(selectedLocation: CLLocationCoordinate2D) {
Task {
let request = MKLookAroundSceneRequest(coordinate: selectedLocation)
lookAroundScene = try? await request.scene
}
}
}
それぞれ詳しくみていきます。
以下の部分ではそれぞれマップのカメラ位置、LookAroundViewer
のシーン、表示非表示の初期値を設定しています。
position
はマップのカメラ位置であり、初期値で大阪城付近に設定しています。
lookAroundScene
はユーザーがマップをタップすることで位置を取得するため、 Nullable な State としています。
isLookAroundPreviewShown
は LookAroundViewer
の表示非表示を保持する State であり、初期値では表示されないため false としています。
@State private var position: MapCameraPosition = .region(.init(
center: .init(latitude: 34.687234, longitude: 135.525842),
span: .init(latitudeDelta: 0.005, longitudeDelta: 0.005)
))
@State private var lookAroundScene: MKLookAroundScene?
@State private var isLookAroundPreviewShown = false
以下の部分ではタップした際に LookAroundViewer
が開くマップを実装しています。
MapReader
で mapProxy
を受け取り、それを onTapGesture
で使用することでタップした位置を取得することができます。
また、後述の requestLookAroundScene
にタップした位置を渡して isPresentedLookAroundPreview
を true にしています。
MapReader { mapProxy in
Map(position: $position)
.onTapGesture { location in
guard let selectedLocation = mapProxy.convert(location, from: .local)
else { return }
requestLookAroundScene(selectedLocation: selectedLocation)
isPresentedLookAroundPreview = true
}
}
以下では LookAroundViewer
の設定を行なっています。
onTapGesture
を持つ MapReader
に対して .lookAroundViewer
を指定することで、タップした際に LookAroundViewer
を開く実装にすることができます。
.lookAroundViewer(
isPresented: $isLookAroundPreviewShown,
scene: $lookAroundScene
)
以下ではマップでタップされた場所を CLLocationCoordinate2D
として受け取り、その座標をもとにして LookAroundViewer
の scene を生成しています。
func requestLookAroundScene(selectedLocation: CLLocationCoordinate2D) {
Task {
let request = MKLookAroundSceneRequest(coordinate: selectedLocation)
lookAroundScene = try? await request.scene
}
}
なお取得した座標から scene を生成できなかった場合は以下のようにグレーの表示になります。
マップの設定
マップに関して以下のように指定することができます。
.lookAroundViewer(
isPresented: $isLookAroundPreviewShown,
scene: $lookAroundScene,
allowsNavigation: false,
showsRoadLabels: false
)
allowsNavigation
はデフォルトでは true になっています。
これを false にすると初めの章で提示した動画のように周りをタップして歩き回ることができなくなります。
また、showsRoadLabels
はデフォルトで true になっていますが、 false にすると以下の画像のように町の名前や道路の名前が表示されなくなります。
showsRoadLabels: true | showsRoadLabels: false |
---|---|
赤く囲った部分に「谷町2」のテキストが表示されています | テキストは表示されません |
今回はマップのカメラ位置を固定して実装しましたが、前の画面から座標を受け取り、 onAppear
で position
に代入することで任意の座標をカメラ位置に指定することができます。
まとめ
最後まで読んでいただいてありがとうございました。
今回実装した LookAroundViewer
では比較的簡単にストリートビューと同じような機能を実装することができました。また、 visionOS ではかなり画面を拡大することができ、より没入感のあるストリートビューを実装できます。
誤っている点やもっと良い書き方があればご指摘いただければ幸いです。
参考
Discussion