SwiftUIでカスタム地図を表示する(その1)
観光地図やらクライムマップやらもっと便利に表示したいなと。
(予備知識)
SwiftUIでiOSアプリを書くには
SwiftUIになって大きく変わったところがGUIの作り方。従来はstoryboardを使ってGUI部品を配置し、そのGUIを制御するUIViewをゴリゴリ書く感じでしたが、SwiftUIになって、GUI部品にレイアウトやら制御やらをまとめた感じ。
SwiftUIで地図を表示するには
SwiftUIにはWKWebViewに相当するViewがないので、UIKitのWKWebViewをSwiftUI用にラップして使用する必要があります。これにはUIViewRepresentableプロトコルを使います。詳しくは公式チュートリアルのとおり。
@k_awoki, "UIViewRepresentableを使ってSwiftUIでちょっとリッチなWebviewを表示してみる", Qiita
@m37335, "【SwiftUI】Mapkitを使った位置情報の取得とピンの表示",Qiita
MapKitの基本
前述のようにMapKitはUIKitで、地図上にGUI部品を載せることができる。地図上の地点に関するGUI部品のアノテーション、画像や図形のGUI部品のオーバレイが用意されている。ここでややこしいことに、GUI部品のオブジェクトと、GUI部品を表示する部品を別々に用意してやらないといけない。
タイルデータを表示するには
カスタムタイルをmapViewにオーバレイする。それには、
(1) タイルオーバーレイを供給するオブジェクトを登録する。
(1) タイルオーバーレイを供給するオブジェクトを登録する。
具体的には、MKTileOverlay()を用いて、地図に重ねるタイルデータ供給オブジェクトをつくり、mapViewオブジェクトにセットする。
(2) タイルオーバーレイをレンダリングするオブジェクトをmapViewオブジェクトにセットする。
(2) タイルオーバーレイをレンダリングするオブジェクトをmapViewオブジェクトにセットする。
具体的には 、mapViewのdelegateオブジェクトに、mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRendererのメソッド を定義する。
すると、mapViewを操作したとき、 タイルデータ供給オブジェクトを用いてタイルデータをダウンロードし、画面の表示範囲にそのタイルデータが含まれたときに、オーバーレイレンダラーが呼び出され、タイルが表示されるようになる。
すると、mapViewを操作したとき、 タイルデータ供給オブジェクトを用いてタイルデータをダウンロードし、画面の表示範囲にそのタイルデータが含まれたときに、オーバーレイレンダラーが呼び出され、タイルが表示されるようになる。
@imk2o,"MapKitで国土地理院のタイルデータを表示する",qiita
PUNIO,"Xamarin.iOSで地図にTileを追加したりしてみる",プログラムの事とか,Hatena Blog
(実際)
MapKitで作った地図GUIを
import SwiftUI
import MapKit
struct UICustomMapView: UIViewRepresentable {
var coordinate:CLLocationCoordinate2D
var coordinator:Coordinator?
let mapView=MKMapView()
// 地理院地図のタイルをセットする
//var tileOverlay = MKTileOverlay(urlTemplate: "https://cyberjapandata.gsi.go.jp/xyz/relief/{z}/{x}/{y}.png")
// open street mapのタイルをセットする
var tileOverlay = MKTileOverlay(urlTemplate: "http://tile.openstreetmap.org/{z}/{x}/{y}.png")
func makeUIView(context: Context) -> MKMapView {
//MKMapView(frame: .zero)
mapView.delegate=context.coordinator
mapView.addOverlay(tileOverlay, level: .aboveLabels)
let span = MKCoordinateSpan(latitudeDelta: 1.0/2, longitudeDelta: 1.0/2)
let region = MKCoordinateRegion(center: coordinate, span: span)
mapView.setRegion(region, animated: true)
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: UICustomMapView
init(_ parent: UICustomMapView) {
self.parent = parent
super.init()
self.parent.coordinator = self
//デフォルトタイルを置き換える
parent.tileOverlay.canReplaceMapContent = true
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
return MKTileOverlayRenderer(overlay: overlay)
}
}
}
struct UICustomMapView_Previews: PreviewProvider {
static var previews: some View {
UICustomMapView(coordinate: CLLocationCoordinate2D( latitude: 35.658807,longitude: 139.745486))
}
}
今回はこんなところで。
コメント
コメントを投稿