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)) } }
今回はこんなところで。
コメント
コメントを投稿