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) タイルオーバーレイを供給するオブジェクトを登録する。
具体的には、MKTileOverlay()を用いて、地図に重ねるタイルデータ供給オブジェクトをつくり、mapViewオブジェクトにセットする。
(2) タイルオーバーレイをレンダリングするオブジェクトをmapViewオブジェクトにセットする。
具体的には 、mapViewのdelegateオブジェクトに、mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRendererのメソッド を定義する。
すると、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))
    }
}

今回はこんなところで。

コメント

このブログの人気の投稿

SwiftUIでカスタム地図を表示する(その2)

カーメン君のガーデンチャンネル「萌木の村」