how to set up array for multi annotations with swift
You could do, for example:
let locations = [
["title": "New York, NY", "latitude": 40.713054, "longitude": -74.007228],
["title": "Los Angeles, CA", "latitude": 34.052238, "longitude": -118.243344],
["title": "Chicago, IL", "latitude": 41.883229, "longitude": -87.632398]
]
for location in locations {
let annotation = MKPointAnnotation()
annotation.title = location["title"] as? String
annotation.coordinate = CLLocationCoordinate2D(latitude: location["latitude"] as! Double, longitude: location["longitude"] as! Double)
mapView.addAnnotation(annotation)
}
Or, alternatively, use a custom type, e.g.:
struct Location {
let title: String
let latitude: Double
let longitude: Double
}
let locations = [
Location(title: "New York, NY", latitude: 40.713054, longitude: -74.007228),
Location(title: "Los Angeles, CA", latitude: 34.052238, longitude: -118.243344),
Location(title: "Chicago, IL", latitude: 41.883229, longitude: -87.632398)
]
for location in locations {
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
mapView.addAnnotation(annotation)
}
Or you can replace that for
loop with map
:
let annotations = locations.map { location -> MKAnnotation in
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
return annotation
}
mapView.addAnnotations(annotations)
How to setup multiple annotations near the same area
simple way would be adding the annotations in an array and then looping through the array and adding the annotations on the map. Also you need to adjust the zoom level of the map.
let locations = [
["title": "New York, NY", "latitude": 40.713054, "longitude": -74.007228],
["title": "Los Angeles, CA", "latitude": 34.052238, "longitude": -118.243344],
["title": "Chicago, IL", "latitude": 41.883229, "longitude": -87.632398]
]
for location in locations {
let annotation = MKPointAnnotation()
annotation.title = location["title"] as? String
annotation.coordinate = CLLocationCoordinate2D(latitude: location["latitude"] as! Double, longitude: location["longitude"] as! Double)
mapView.addAnnotation(annotation)
}
display multiple annotations on mapview
This should work for you.
By the way, looping through your locations whilst in the events loop is just going to add duplicates of the same pin, which will be very bad on memory and it's not a good thing to do.
import UIKit
import MapKit
import Firebase
class ViewController: UIViewController, MKMapViewDelegate {
@IBOutlet weak var map: MKMapView!
let locationManager = CLLocationManager()
var events = Array<Event>()
var locations = Array<Location>()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
// This closure gets called once for every event in your database
getEvents { (event, pin) in
self.tableView.reloadData()
if event != nil && pin != nil {
print(event!)
self.locations.append(pin!)
self.events.append(event!)
} else {
print("Failed to get event.")
}
}
}
func getEvents(results: @escaping (_ event: Event?, _ pin:Location?)->()) {
let ref = FIRDatabase.database().reference().child("Events")
ref.observe(.childAdded, with: { (snapshot) in
guard let value = snapshot.value as? Dictionary<String,String> else { return }
guard let name = value["eventName"],
let location = value["location"],
let attending = value["attendance"],
let dateTime = value["dateTime"],
let addedByUser = value["addedByUser"] else { return }
self.getEventPlacemark(address: location, results: { (placemark) in
if let placemark = placemark {
let pin = Location(title: name, coordinate: placemark.coordinate)
var distance: Double = -1 // If user location is not avalible, this will stay at -1
if let currentLocation = self.locationManager.location?.coordinate {
distance = pin.coordinate.distance(to: currentLocation)
}
let event = Event(id: snapshot.key, name: name, location: location, dateTime: dateTime, addedByUser: addedByUser, attending: attending, distance: distance)
results(event, pin)
return
}
})
})
}
func getEventPlacemark(address:String, results: @escaping (_ placemark: MKPlacemark?)->()){
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
if let error = error {
print(error.localizedDescription)
results(nil)
}
if let placemark = placemarks?.first {
results(MKPlacemark(placemark: placemark))
}
}
}
func displayAllEvents(){
self.map.addAnnotations(self.locations)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let userLocation = annotation as? MKUserLocation {
let view = (mapView.dequeueReusableAnnotationView(withIdentifier: "userLocationPin") as? MKPinAnnotationView) ?? MKPinAnnotationView(annotation: userLocation, reuseIdentifier: "userLocationPin")
view.pinTintColor = .purple
view.canShowCallout = true
return view
}
if let pin = annotation as? MKPointAnnotation {
let view = (mapView.dequeueReusableAnnotationView(withIdentifier: "pin") as? MKPinAnnotationView) ?? MKPinAnnotationView(annotation: pin, reuseIdentifier: "pin")
view.pinTintColor = .red
view.canShowCallout = true
return view
}
return nil
}
}
class Location: NSObject, MKAnnotation {
var title: String?
var coordinate: CLLocationCoordinate2D
init(title: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.coordinate = coordinate
}
}
struct Event {
var name: String
var location: String
}
extension CLLocationCoordinate2D {
func distance(to: CLLocationCoordinate2D) -> CLLocationDistance {
return MKMetersBetweenMapPoints(MKMapPointForCoordinate(self), MKMapPointForCoordinate(to))
}
}
If you are going to implement my code above, you'll need to remove your code from
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
because you don't need to add the pin manually anymore.
Can you try putting this in your view did load function and not have anything else run. This should put a pin to the west of Africa.
Something to notice, if the coordinate is
CLLocationCoordinate2D(latitude: 0, longitude: 0), the pin appears
right down at Antartica on the date line, which is the bottom right of
the map. So if for some reason your coordinate is the default 0,0,
your pins might by sitting there.
let pin = MKPointAnnotation()
pin.title = "Null Island"
pin.coordinate = CLLocationCoordinate2DMake(0.000001, 0.000001)
self.map.addAnnotation(pin)
This could possibly be where your pins are.
How can I create multiple custom annotations in a map view?
It's been a long time since I've used an MKMapView and custom annotations (it was in Objective-C.)
I seem to remember that in your mapView(_:viewFor:)
function you need to test to see if the annotation being passed to you is an MKUserLocation. If it is, return nil:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard !annotation is MKUserLocation else {
//This is the user location. Return nil so the system uses the blue dot.
return nil
}
//Your code to create an return custom annotations)
}
Dealing with multiple annotations at the same address in an MGLMapView using SwiftUI and Mapbox
I ended up doing my own implementation--I made an array of annotations with the same latitude and longitude and then I added buttons to the custom callout to cycle through that array, keeping track of the annotation that I am looking at.
How to add multiple annotations in map view
You can do it by manipulating MKMapView's delegate
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
print("viewForAnnotation \(annotation.title)")
if annotation is MKUserLocation {
return nil
}
let reuseID = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseID) as? MKPinAnnotationView
if(pinView == nil) {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseID)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
}
return pinView
}
Related Topics
How to Work with Udp Sockets in iOS, Swift
How to Properly Handle a Nil Uiapplication.Sharedapplication().Keywindow
How to Update Particular Value of Child in Firebase Db
Avcapturevideopreviewlayer Add Overlays and Capture Photo in iOS
Calculate New Coordinates with Starting Position and Distance
Sirikit, How to Display Response for Start Workout Intent
Dynamically Passing Closure with Keypaths to a Sorting Function
Match the Data Type of a Object in Swift
Using Huffman Coding to Compress Images Taken by the iPhone Camera
Unrecognized Selector Sent to Instance Swift 3
What Is a Safe Way to Turn Streamed (Utf8) Data into a String
Swift Bridging Header File Won't Work with Use_Frameworks
Find Delegate in a Swift Array of Delegates
iOS Firebase Database Get Key of Value
Firebase: Provided Bucket Does Not Match the Storage Bucket of the Current Instance in Swift