🌍

Mapkitで検索した場所を表示する

2024/06/16に公開

対象者

  • SwiftUIに興味ある人
  • Mapkit使ってる人
  • 検索機能をつけたい

プロジェクトの説明

SwiftUiのMapkitを使うと、地図を表示することができます。位置を検索する機能は実装する必要がありますが.....

動作は微妙だが動くものを作ってみた。

https://youtube.com/shorts/aUYNPMwm-Aw?feature=share

  • 実装した機能
    • 画面上の検索機能
    • 画面のどこかをタップすると、キーボードを閉じる

[Example Code]

import SwiftUI
import MapKit

struct ContentView: View {
    @State private var searchText = ""
    @State private var landmarks = [Landmark]()

    var body: some View {
            VStack {
                // 検索バー
                SearchBar(text: $searchText, onSearchButtonChanged: { self.searchLandmarks() })
                // 地図表示
                MapView(landmarks: $landmarks)
            }
            .onAppear {
                // 初期表示時に東京駅を検索
                searchLandmarks(for: "東京駅")
            }
            .onTapGesture {
            // 画面をタップしたらキーボードを閉じる
            UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
        }
        }

    private func searchLandmarks(for place: String = "") {
        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = place.isEmpty ? searchText : place

        let search = MKLocalSearch(request: request)
        search.start { (response, error) in
            guard let response = response else { return }
            landmarks = response.mapItems.map {
                Landmark(placemark: $0.placemark)
            }
        }
    }
}

struct Landmark {
    let placemark: MKPlacemark

    var id: String {
        placemark.name ?? UUID().uuidString
    }

    var name: String {
        placemark.name ?? ""
    }

    var coordinate: CLLocationCoordinate2D {
        placemark.coordinate
    }
}

struct SearchBar: UIViewRepresentable {
    @Binding var text: String
    var onSearchButtonChanged: () -> Void

    func makeUIView(context: Context) -> UISearchBar {
        let searchBar = UISearchBar(frame: .zero)
        searchBar.delegate = context.coordinator
        return searchBar
    }

    func updateUIView(_ uiView: UISearchBar, context: Context) {
        uiView.text = text
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UISearchBarDelegate {
        var parent: SearchBar

        init(_ parent: SearchBar) {
            self.parent = parent
        }

        func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
            parent.text = searchBar.text ?? ""
            parent.onSearchButtonChanged()
        }
    }
}

struct MapView: UIViewRepresentable {
    @Binding var landmarks: [Landmark]

    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }

    func updateUIView(_ uiView: MKMapView, context: Context) {
        updateAnnotations(from: uiView)
    }

    private func updateAnnotations(from mapView: MKMapView) {
        mapView.removeAnnotations(mapView.annotations)
        let annotations = landmarks.map(LandmarkAnnotation.init)
        mapView.addAnnotations(annotations)
        if let firstLandmark = landmarks.first {
            let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
            let region = MKCoordinateRegion(center: firstLandmark.coordinate, span: span)
            mapView.setRegion(region, animated: true)
        }
    }
}

class LandmarkAnnotation: NSObject, MKAnnotation {
    let id: String
    let name: String
    var coordinate: CLLocationCoordinate2D

    init(landmark: Landmark) {
        self.id = landmark.id
        self.name = landmark.name
        self.coordinate = landmark.coordinate
    }

    var title: String? {
        return name
    }
}

感想

今回は、マップアプリに検索機能をつけるのをやってみました。まだまだ改良が必要ですが、参考になりそうなので、記録を残す目的で記事を書いてみました。

Discussion