📍

【SwiftUI】あらかじめ作成したリストからマップ上にピンを打つ

2023/04/24に公開

どういうことか

なんらかの住所リストとして、例えば nameaddress が羅列されたJSONがある。
例えばこんなの👇

spots_data.json
[
    {
        "id": 1,
        "name": "アルサーガパートナーズ株式会社",
        "address": "東京都渋谷区道玄坂1丁目12−1"
    }
]

MapKit を使い、こちらのリストの住所の座標にピンを打つ。

JSONをデコード

SpotViewModel
    @Published var spots: [Spot] = Bundle.main.decode("spotsData.json")

変換したリストから住所のみを抽出したリストを作る

SpotViewModel
    // 住所のみを抽出したリスト
    private var addressList: [String] = []
    
    func createAddressList(from spots: [Spot]) {
        addressList = spots.compactMap{ $0.address }
    }

座標の構造体を作る

CoordinateEntity
import Foundation
import CoreLocation

struct Coordinate: Identifiable {
    let id = UUID()
    let latitude: Double
    let longitude: Double
    var coodinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: latitude,
            longitude: longitude
        )
    }
}

住所から座標を取得

SpotViewModel
    // 座標のリスト
    @Published var coordinates: [Coordinate] = []
    
    let geocoder = CLGeocoder()

    func fetchCoordinats(addresses: [String]) {
        for address in addresses {
            geocoder.geocodeAddressString(address) {
                placemarks, error in
                if let error = error {
                    print("座標取得エラー; \(error.localizedDescription)")
                    return
                }
                if let placemark = placemarks?.first {
                    if let location = placemark.location {
                        let coordinate = Coordinate(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
                        self.coordinates.append(coordinate)
                    }
                }
            }
        }
    }

起動時に呼び出す

SpotViewModel
    init() {
        createAddressList(from: spots)
        fetchCoordinats(addresses: addressList)
    }

マップ上にピンを打つ

ContentView
    // 中略

    // マップの描写
    var body: some View {
        ZStack {
            Map(
                coordinateRegion: $region,
                annotationItems: viewModel.coordinates,
                annotationContent: { spot in
                    MapAnnotation (coordinate: spot.coodinate) {
                        ZStack {
                            SpotIcon()
                        }
                    }
                }
            )
            .edgesIgnoringSafeArea(.all)
        }
    }

(一応)スポットのアイコンは別ファイルで作成したものを使う。
結果、設定した座標にemojiと黒丸が重なったアイコンが表示されるようになる。

SpotIcon
import SwiftUI

struct SpotIcon: View {
    var body: some View {
        Text("🏢")
            .frame(width: 40, height: 40)
            .background(Color.yellow)
            .cornerRadius(30.0)
            .shadow(color: .gray, radius: 3, x: 3, y: 3)
    }
}

struct SpotIcon_Previews: PreviewProvider {
    static var previews: some View {
        SpotIcon()
    }
}

実際に表示してみよう

おわり(´・ω・`)

Arsaga Developers Blog

Discussion