I need to add multiple markers in my MapView. How to add multiple markers in same mapview using swiftui?
This is my code:
import SwiftUI
import UIKit
import GoogleMaps
struct MapView: UIViewRepresentable {
let coordinate: CLLocationCoordinate2D?
let marker : GMSMarker = GMSMarker()
func makeUIView(context: Self.Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: coordinate.latitude, longitude: coordinate.longitude, zoom: 6.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: Self.Context) {
marker.position = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
marker.title = "XYZ"
marker.snippet = "ABCD"
marker.map = mapView
}
}
Here is my sample,
struct MapView: UIViewRepresentable {
let coordinate: CLLocationCoordinate2D
let cities = [
[
"name": "Yangon",
"lat": 16.8409,
"long": 96.1735
],
[
"name": "Mandalay",
"lat": 21.9588,
"long": 96.0891
]
]
func makeUIView(context: Self.Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: coordinate.latitude, longitude: coordinate.longitude, zoom: 6.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: Self.Context) {
for city in cities {
let marker : GMSMarker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: city["lat"] as! CLLocationDegrees, longitude: city["long"] as! CLLocationDegrees)
marker.title = city["name"] as? String
marker.snippet = "Welcome to \(city["name"] as! String)"
marker.map = mapView
}
}
}
Related
I was trying to display my current location into a Swiftui MapView. To do so, I created the following class:
import SwiftUI
import CoreLocation
import Combine
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
#Published var locationManager = CLLocationManager()
#Published var locationStatus: CLAuthorizationStatus?
#Published var lastLocation: CLLocation?
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
private var statusString: String {
guard let status = locationStatus else {
return "unknown"
}
switch status {
case .notDetermined: return "notDetermined"
case .authorizedWhenInUse: return "authorizedWhenInUse"
case .authorizedAlways: return "authorizedAlways"
case .restricted: return "restricted"
case .denied: return "denied"
default: return "unknown"
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationStatus = status
print(#function, statusString)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
lastLocation = location
// fetchLocation(location: lastLocation)
self.locationManager.stopUpdatingLocation()
print(#function, location)
}
}
and the following view:
var body: some View {
VStack {
Text("Latitude: \(locationManager.lastLocation?.coordinate.latitude ?? 0), Longitude: \(locationManager.lastLocation?.coordinate.longitude ?? 0)")
.onAppear{
print("DEBUG: status 1 : \(locationManager.lastLocation?.coordinate.latitude ?? 0)")
}
MapView(lat: (locationManager.lastLocation?.coordinate.latitude ?? 0), lon: locationManager.lastLocation?.coordinate.longitude ?? 0, latDelta: 0.05, lonDelta: 0.05)
.frame(width: UIScreen.screenWidth - 36, height: UIScreen.screenWidth / 2)
.cornerRadius(10)
.onAppear{
print("DEBUG: status 2 : \(locationManager.locationStatus)")
print("DEBUG: lat: \(locationManager.lastLocation?.coordinate.latitude), lon: \(locationManager.lastLocation?.coordinate.longitude)")
}
}
}
So far, the Textfield does show the correct coordinates, but my Mapview shows NIL as the coordinates.
Adding my MapView here as well for completeness:
struct MapView: UIViewRepresentable {
#State var lat = 0.0
#State var lon = 0.0
#State var latDelta = 0.05
#State var lonDelta = 0.05
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Context) {
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon )
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
}
Hope you could have a look and see if I have missed anything.
The answer is:
Remove all the #State from the MapView():
struct MapView: UIViewRepresentable {
var lat = 0.0
var lon = 0.0
var latDelta = 0.05
var lonDelta = 0.05
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Context) {
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon )
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
}
can someone please help me with my problem?
I have a SwiftUI Project and wrote a MKMapView File. I would like to store the Center coordinates in a #EnvironmentObject variable.
Unfortunately, I can't manage to pack the center coordinates into the EnvironmentObject variable.
my code looks like this:
struct MapView: UIViewRepresentable {
#EnvironmentObject var messpunkt_manager: Messpunkt_Manager
func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.showsUserLocation = true // zeigt den aktuellen Standort des Users
map.delegate = context.coordinator // ermöglich die Bedienung
// shows annotations already in array
for location in messpunkt_manager.messpunkte {
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
map.addAnnotation(annotation)
}
return map
}
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {
// start coordinates
let coordinate = CLLocationCoordinate2D(latitude: messpunkt_manager.centerCoordinate.latitude, longitude: messpunkt_manager.centerCoordinate.longitude)
let span = MKCoordinateSpan(latitudeDelta: messpunkt_manager.centerZoom.latitudeDelta, longitudeDelta: messpunkt_manager.centerZoom.longitudeDelta)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
// Adds annotation to the map
for location in messpunkt_manager.messpunkte {
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
uiView.addAnnotation(annotation)
}
if messpunkt_manager.karteAusgeaehlt == 0 {
uiView.mapType = .standard
}
if messpunkt_manager.karteAusgeaehlt == 1 {
uiView.mapType = .hybrid
}
if messpunkt_manager.karteAusgeaehlt == 2 {
uiView.mapType = .satellite
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
the center coordinates should then be put into this #EnvironmentObject variable
#Published var position = CLLocationCoordinate2D()
thanks for your help!
And sorry for my naming. For me its easier to name the variables, functions ... in my native language German. I hope its not a big deal.
In your class Coordinator you need to set the value. Assuming your coordinator is set up like this:
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
...
}
Then you would add this function:
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
parent. messpunkt_manager.position = mapView.region
}
It is really helpful to post a Minimal Reproducible Example (MRE). As you can see, you left out an important piece
I have custom Map with pin on user location. When i need to show user location. I tried use CLLocationManager to show coordinates but they turns 0.0
struct MapView : UIViewRepresentable {
func makeCoordinator() -> MapView.Coordinator {
return MapView.Coordinator(parent1: self)
}
#Binding var title : String
#Binding var subtitle : String
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let map = MKMapView()
let coordinate = CLLocationCoordinate2D(latitude: 55.757485,
longitude: 37.632179)
map.region = MKCoordinateRegion(center: coordinate,
latitudinalMeters: 100,
longitudinalMeters: 100)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
map.delegate = context.coordinator
map.addAnnotation(annotation)
return map
}
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {}
class Coordinator: NSObject , MKMapViewDelegate {
var parent : MapView
init(parent1: MapView) {
parent = parent1
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let pin = MKPinAnnotationView(annotation: annotation,
reuseIdentifier: "pin")
pin.isDraggable = true
pin.pinTintColor = .blue
pin.animatesDrop = true
return pin
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, didChange newState: MKAnnotationView.DragState, fromOldState oldState: MKAnnotationView.DragState) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: (view.annotation?.coordinate.latitude)!,
longitude: (view.annotation?.coordinate.longitude)!)){ (places, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
self.parent.title = (places?.first?.name ?? places?.first?.postalCode)!
self.parent.subtitle = (places?.first?.locality ?? places?.first?.country ?? "None")
}
}
}
}
There is my start point
let coordinate = CLLocationCoordinate2D(latitude: 55.757485,
longitude: 37.632179)
So i need to add user coordinates in this stroke without asking permit as i have already asked it
All you have to do is make the showsUserLocation var true.(Look at the docs)
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let map = MKMapView()
let coordinate = CLLocationCoordinate2D(latitude: 55.757485, longitude: 37.632179)
map.region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 100, longitudinalMeters: 100)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
// new code...
map.showsUserLocation = true
map.delegate = context.coordinator
map.addAnnotation(annotation)
return map
}
As of iOS 14 you can use the native SwiftUI Map. This might be easier to work with than bridging to UIKit
My goal is clustering annotation on map with show number of items in cluster, I have no experience in UIKit and try to avoid it. Is it possible to do it using swiftUI only? If not how to reduce intervention of UIKit?
This is how it should look like
import SwiftUI
import MapKit
struct ContentView: View {
#State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 43.64422936785126, longitude: 142.39329541313924),
span: MKCoordinateSpan(latitudeDelta: 1.5, longitudeDelta: 2)
)
var body: some View {
Map(coordinateRegion: $region, annotationItems: data) { annotation in
MapAnnotation(coordinate: annotation.coordinate) {
Image(systemName: "person.circle.fill")
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(Color.purple)
}
}
.edgesIgnoringSafeArea(.all)
}
}
struct SampleData: Identifiable {
var id = UUID()
var latitude: Double
var longitude: Double
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: latitude,
longitude: longitude)
}
}
var data = [
SampleData(latitude: 43.70564024126748, longitude: 142.37968945214223),
SampleData(latitude: 43.81257464206404, longitude: 142.82112322464369),
SampleData(latitude: 43.38416585162576, longitude: 141.7252598737476),
SampleData(latitude: 45.29168643283501, longitude: 141.95286751470724),
SampleData(latitude: 45.49261392585982, longitude: 141.9343973160499),
SampleData(latitude: 44.69825427301145, longitude: 141.91227845284203)
]
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I find the way to cluster annotations with MapKit, but reuse map like a view for easy swiftUI. Looks like that https://i.stack.imgur.com/u3hKR.jpg
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
var forDisplay = data
#State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 43.64422936785126, longitude: 142.39329541313924),
span: MKCoordinateSpan(latitudeDelta: 1.5, longitudeDelta: 2)
)
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
/// showing annotation on the map
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? LandmarkAnnotation else { return nil }
return AnnotationView(annotation: annotation, reuseIdentifier: AnnotationView.ReuseID)
}
}
func makeCoordinator() -> Coordinator {
MapView.Coordinator(self)
}
func makeUIView(context: Context) -> MKMapView {
/// creating a map
let view = MKMapView()
/// connecting delegate with the map
view.delegate = context.coordinator
view.setRegion(region, animated: false)
view.mapType = .standard
for points in forDisplay {
let annotation = LandmarkAnnotation(coordinate: points.coordinate)
view.addAnnotation(annotation)
}
return view
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
struct SampleData: Identifiable {
var id = UUID()
var latitude: Double
var longitude: Double
var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: latitude,
longitude: longitude)
}
}
var data = [
SampleData(latitude: 43.70564024126748, longitude: 142.37968945214223),
SampleData(latitude: 43.81257464206404, longitude: 142.82112322464369),
SampleData(latitude: 43.38416585162576, longitude: 141.7252598737476),
SampleData(latitude: 45.29168643283501, longitude: 141.95286751470724),
SampleData(latitude: 45.49261392585982, longitude: 141.9343973160499),
SampleData(latitude: 44.69825427301145, longitude: 141.91227845284203)
]
class LandmarkAnnotation: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
init(
coordinate: CLLocationCoordinate2D
) {
self.coordinate = coordinate
super.init()
}
}
/// here posible to customize annotation view
let clusterID = "clustering"
class AnnotationView: MKMarkerAnnotationView {
static let ReuseID = "cultureAnnotation"
/// setting the key for clustering annotations
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
clusteringIdentifier = clusterID
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForDisplay() {
super.prepareForDisplay()
displayPriority = .defaultLow
}
}
And use that map like a default view
import SwiftUI
struct ContentView: View {
var body: some View {
MapView()
.edgesIgnoringSafeArea(.all)
}
}
For solving a problem I used next resources:
https://www.hackingwithswift.com/books/ios-swiftui/communicating-with-a-mapkit-coordinator
https://www.hackingwithswift.com/books/ios-swiftui/advanced-mkmapview-with-swiftui
https://developer.apple.com/videos/play/wwdc2017/237/
https://www.youtube.com/watch?v=QuYA7gQjTt4
Here's my code. As soon as I fire up the app in Simulator. I see following messages in the debug window.
2020-05-02 23:01:37.931720-0400 SwiftMapView[42245:6851218] Metal API Validation Enabled
2020-05-02 23:01:38.216200-0400 SwiftMapView[42245:6851218] libMobileGestalt MobileGestalt.c:1647: Could not retrieve region info
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
var locationManager = CLLocationManager()
func setupManager() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
//locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
// Set Geofencing region
//let locValue:CLLocationCoordinate2D = self.locationManager.location!.coordinate
let locValue:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 15.833826, longitude: 78.845220)
let geofencingRegion: CLCircularRegion = CLCircularRegion(center:locValue, radius: 100, identifier: "Crap")
geofencingRegion.notifyOnExit = true
geofencingRegion.notifyOnEntry = true
// Start monitoring
locationManager.startMonitoring(for: geofencingRegion)
}
func makeUIView(context: Context) -> MKMapView {
setupManager()
let mapView = MKMapView(frame: UIScreen.main.bounds)
//mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 15.833826, longitude: 78.845220)
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
// Show current user location
//uiView.showsUserLocation = true
// Requst location when in use
self.locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled()
&& (CLLocationManager.authorizationStatus() == .authorizedAlways || CLLocationManager.authorizationStatus() == .authorizedWhenInUse)
{
//self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.startUpdatingLocation()
let locValue:CLLocationCoordinate2D = self.locationManager.location!.coordinate
let coordinate = CLLocationCoordinate2D(latitude: locValue.latitude, longitude: locValue.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0, longitudeDelta: 0)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
}
}
class MapAppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.locationManager = CLLocationManager()
self.locationManager!.delegate = self
return true
}
}
extension MapAppDelegate{
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Hello World")
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Welcome Home")
}
}
In simulator you have to add a location, then your code works for me (at least if your info.plist is correct):