MKPinAnnotation to Detailed view - ios

I have an object array from a PFQuery and then add MKPinAnnotations in my mapView with the following function:
func addAnnotation() {
for (var i = 0; i < self.objectArray.count; i++) {
var location = object.objectForKey("location") as PFGeoPoint
var annotation = MKPointAnnotation()
annotation.setCoordinate(CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
annotation.title = object.objectForKey("title") as NSString
var priceFormatter = NSNumberFormatter()
self.mapView.addAnnotation(annotation)
}
}
I have want to be able to click on the pin (or the information tab that shows when the pin is tapped) and segue to a detail view controller and pass the information associated with that pin or that index place in the Array...
I only need to know how to get the index number of the tapped pin so I can perform the segue...I've been stuck in this piece of code for the past 3 days and gave up and decided to ask the masters :P
Hope you can help me out please.
Thanks

Create a custom class for your annotation where you can store additional information such as the array index:
class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
var title: String = ""
var subtitle: String = ""
var index: Int = 0
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
}
later in your code you can set the array index:
func addAnnotation() {
for (var i = 0; i < self.objectArray.count; i++) {
var location = object.objectForKey("location") as PFGeoPoint
var annotation = PinAnnotation()
annotation.setCoordinate(CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
annotation.title = object.objectForKey("title") as NSString
annotation.index = i
var priceFormatter = NSNumberFormatter()
self.mapView.addAnnotation(annotation)
}
}
You have access to all the properties when you are working with the annotation object

Related

How to Update Annotation Coordinates on MapView

I have a mapView and vehicle coordinates which changes in every 15 seconds so I want to update the coordinates. My current approach is to delete all annotations and adding new ones. However, I can't use animations in that approach, they are just spawning. When I looked google I've found out that people just changing the coordinates of annotations and it's happening. Not for me unfortunately.
Old version:
func updateVehicleLocations(){
let annotations = mapView.annotations.filter {
$0.title != "person"
}
mapView.removeAnnotations(annotations)
for vehicle in vehicles {
let pin = MKPointAnnotation()
pin.coordinate = CLLocationCoordinate2D(latitude: vehicle.latitude, longitude: vehicle.longitude)
pin.title = vehicle.vehicleID
mapView.addAnnotation(pin)
}
if isSetCoordinatesMoreThanOnce { return }
mapView.showAnnotations(mapView.annotations, animated: true)
isSetCoordinatesMoreThanOnce = true
}
My test:
func updateVehicleLocations(){
for annotation in busAnnotations {
UIView.animate(withDuration: 0.5) { [self] in
if let vehicle = self.vehicles.first(where: { $0.vehicleID == annotation.vehicleID }) {
annotation.coordinate = CLLocationCoordinate2D(latitude: vehicle.latitude, longitude: vehicle.longitude)
}
}
}
if isSetCoordinatesMoreThanOnce { return }
for vehicle in vehicles {
let pin = BusAnnotation(coordinate: CLLocationCoordinate2D(latitude: vehicle.latitude, longitude: vehicle.longitude), vehicleID: vehicle.vehicleID)
busAnnotations.append(pin)
}
mapView.addAnnotations(busAnnotations)
mapView.showAnnotations(mapView.annotations, animated: true)
isSetCoordinatesMoreThanOnce = true
}
That was exhausting, I still don't get the logic but here is how I fixed it;
I had to use a custom class(of MKAnnotation) to set coordinates otherwise the coordinate property is read-only. So, here is an example custom annotation class;
final class BusAnnotation: NSObject, MKAnnotation{
var vehicleID: String
var coordinate: CLLocationCoordinate2D
init(coordinate: CLLocationCoordinate2D, vehicleID:String) {
self.coordinate = coordinate
self.vehicleID = vehicleID
super.init()
}
}
however, it doesn't update coordinates, to fix it we need to add "dynamic" keyword to coordinate property everything works fine!
final class BusAnnotation: NSObject, MKAnnotation{
var vehicleID: String
dynamic var coordinate: CLLocationCoordinate2D
init(coordinate: CLLocationCoordinate2D, vehicleID:String) {
self.coordinate = coordinate
self.vehicleID = vehicleID
super.init()
}
}

Update realm IgnoredProperties While App Running

class place :Object{
#objc dynamic var name : String = ""
#objc dynamic var address :String = ""
#objc dynamic var lat :Double = 0.0
#objc dynamic var long :Double = 0.0
var marker = GMSMarker()
var cllCorrdinate : CLLocationCoordinate2D {
return CLLocationCoordinate2D(latitude: lat, longitude: long)
}
}
when i add a place to an array of FirstViewController
let place = Place()
place.name = "123"
place.address = "123 street"
place.lat = lat
place.long = lon
let marker = GMSMarker()
marker.position = place.cllCorrdinate
marker.icon = UIImage(imageLiteralResourceName: "123")
place.marker = marker
place.marker.map = mapView
saveToRealm(place: place)
I am Sharing data between two ViewController with singleton class and keep updating it through viewWillDisappear and viewDidApear
class anotherController:UITableViewController{
var places:List<PLace>?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let place = places[indexPath.row]
let marker = place.marker
}}
Value of marker always showing nil.
is it possible to update ignore property only while app is running ?
places are saving and updating fine in realm

Is there a preset 'index' or something similar that allows me to refer to specific annotations that a user created? Swift 4

I have an app where the user can search for locations, tap on the row in the table view, and an annotation will be placed at the placemark. I have a button, "meetUpButton" that allows them to put multiple annotations, rather than what my code does by default which is remove the annotation if a new search result is clicked. Is there a way to refer to a specific annotation that my user created, even if they made multiple? For example, say I want to add the latitude of two annotations that my user created...
If I have
let annotation = MKPointAnnotation()
annotation.coordinate.latitude
Is there a way to refer to something like the first annotation with an index of 0 and the next one that my user chose with a 1 or another way? Here's some of my code which might make this clearer.
extension ViewController: handleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark) {
resultSearchController?.isActive = false
// cache the pin
selectedPin = placemark
// clear existing pins
if meetUpOutlet.isEnabled == true {
mapView.removeAnnotations(mapView.annotations)
} else {
}
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let city = placemark.locality,
let state = placemark.administrativeArea {
annotation.subtitle = "(\(city)) (\(state))"
}
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegionMake(placemark.coordinate, span)
mapView.setRegion(region, animated: true)
}
}
You can try to use annotations property of MKMapView instance , to get all use
let anns = self.mapView.annotations
You can implement Custom annotation class.
e.g.
class MyAnnotation : NSObject, MKAnnotation {
var coordinate : CLLocationCoordinate2D
var title: String?
var subtitle: String?
var index: Int?
init(location coord:CLLocationCoordinate2D) {
self.coordinate = coord
super.init()
}
}
Usage:
let annotation = MyAnnotation(location: coordinate)
annotation.title = "title"
annotation.subtitle = "subtitle"
annotation.index = 1
Remove annotations (only index == 1)
for annotation in self.mapView.annotations {
guard let annotation = annotation as? MyAnnotation else {
continue
}
if annotation.index == 1 {
self.mapView.removeAnnotation(annotation)
}
}

Mapview - protection against coordinates found nil

I have mapview that downloads records off CloudKit. The coordinates of each record is based on forward geocoder, where users add the address (ex: New York, NY) and lats and lons are obtained
Current Model is as follow:
class Place: NSObject
{
var name: String
var address: String
var comment: String?
var photo: UIImage?
var rating: Int
var location: CLLocation?
var identifier: String
var record: CKRecord!
init(record: CKRecord)
{
self.record = record
self.name = record.valueForKey(placeName) as! String
self.address = record.valueForKey(placeAddress) as! String
self.comment = record.valueForKey(placeComment) as? String
if let photoAsset = record.valueForKey(placePhoto) as? CKAsset
{
self.photo = UIImage(data: NSData(contentsOfURL: photoAsset.fileURL)!)
}
self.rating = record.valueForKey(placeRating) as! Int
self.location = record.valueForKey(placeLocation) as? CLLocation
self.identifier = record.recordID.recordName
}
// MARK: Map Annotation
var coordinate: CLLocationCoordinate2D {
get {
return location!.coordinate
}
}
This is my method to place each pin on the mapview.
func placePins()
{
for place: Place in self.places
{
let location = CLLocationCoordinate2DMake(place.coordinate.latitude, place.coordinate.longitude)
let dropPin = CustomPointAnnotation(place: place)
dropPin.pinCustomImageName = "customPin"
dropPin.coordinate = location
dropPin.title = place.title
dropPin.subtitle = place.subtitle
dropPin.name = place.name
dropPin.image = place.photo
mapView.addAnnotation(dropPin)
}
}
How do i fix them to protect against any record that doesn't have coordinates since forward geocoder is not the most reliable way?
What about
for place: Place in self.places
{
if (place.location == nil) {
continue;
}
...
}
Not sure what is the issue there

Swift 2 MapKit Annotations - How to group annotations?

I have close to 8.000 annotations in my map and I'd like to group them depending of the zoom that the user do in the app.
All the latitudes and longitudes are already into the CoreData as Double.
Case I have two different annotation images in the same point, I'd like to show two groups, one with the cross and one with the heart.
When the user click over the annotation and if this annotation is a grouped annotation, I'd like to show to the user how many annotations has at this location, "forcing" the user to zoom in to see the annotations individually.
Bellow are the images of my app and the current annotations.
Thank you!
I already got it.
var zoomLevel = Double()
var iphoneScaleFactorLatitude = Double()
var iphoneScaleFactorLongitude = Double()
var CanUpdateMap: Bool = false
static func getLatitudeLongitudeLimitsFromMap(mapView: MKMapView) -> [String: Double] {
var coord = [String: Double]()
let MinLat: Double = mapView.region.center.latitude - (mapView.region.span.latitudeDelta / 2)
let MaxLat: Double = mapView.region.center.latitude + (mapView.region.span.latitudeDelta / 2)
let MinLon: Double = mapView.region.center.longitude - (mapView.region.span.longitudeDelta / 2)
let MaxLon: Double = mapView.region.center.longitude + (mapView.region.span.longitudeDelta / 2)
coord["MinLat"] = MinLat
coord["MaxLat"] = MaxLat
coord["MinLon"] = MinLon
coord["MaxLon"] = MaxLon
return coord
}
func LoadMap(mapView: MKMapView) {
// Get the limits after move or resize the map
let coord: [String: Double] = getLatitudeLongitudeLimitsFromMap(mapView)
let MinLat: Double = coord["MinLat"]! as Double
let MaxLat: Double = coord["MaxLat"]! as Double
let MinLon: Double = coord["MinLon"]! as Double
let MaxLon: Double = coord["MaxLon"]! as Double
var arrAnnotations = [MKAnnotation]()
let FilterMinLat = arrDicListPinsWithLatitudeLongitude.filter({
if let item = $0["Latitude"] as? Double {
return item > MinLat
} else {
return false
}
})
let FilterMaxLat = FilterMinLat.filter({
if let item = $0["Latitude"] as? Double {
return item < MaxLat
} else {
return false
}
})
let FilterMinLon = FilterMaxLat.filter({
if let item = $0["Longitude"] as? Double {
return item > MinLon
} else {
return false
}
})
let FilterMaxLon = FilterMinLon.filter({
if let item = $0["Longitude"] as? Double {
return item < MaxLon
} else {
return false
}
})
for Item in FilterMaxLon {
let dic:[String:AnyObject] = Item
var Name = String()
var Address = String()
var IconPNG = String()
if let Latitude = dic["Latitude"] as? Double {
if let Longitude = dic["Longitude"] as? Double {
if let item = dic["Name"] {
Name = item as! String
}
if let item = dic["Address"] {
Address = item as! String
}
if let item = dic["TypeID"] as? Int {
if item == 11 {
IconPNG = "icon-cross.png"
} else {
IconPNG = "icon-heart.png"
}
}
arrAnnotations.append(CreateAnnotation(Address, Title: Name, Latitude: Latitude, Longitude: Longitude, IconPNG: IconPNG))
}
}
}
}
// Show in the map only the annotations from that specific region
iphoneScaleFactorLatitude = mapView.region.center.latitude
iphoneScaleFactorLongitude = mapView.region.center.longitude
if zoomLevel != mapView.region.span.longitudeDelta {
filterAnnotations(arrAnnotations)
zoomLevel = mapView.region.span.longitudeDelta
CanUpdateMap = true
}
}
func filterAnnotations(arrAnnotations: [MKAnnotation]) {
let latDelta: Double = 0.04 / iphoneScaleFactorLatitude
let lonDelta: Double = 0.04 / iphoneScaleFactorLongitude
var shopsToShow = [AnyObject]()
var arrAnnotationsNew = [MKAnnotation]()
for var i = 0; i < arrAnnotations.count; i++ {
let checkingLocation: MKAnnotation = arrAnnotations[i]
let latitude: Double = checkingLocation.coordinate.latitude
let longitude: Double = checkingLocation.coordinate.longitude
var found: Bool = false
for tempPlacemark: MKAnnotation in shopsToShow as! [MKAnnotation] {
if fabs(tempPlacemark.coordinate.latitude - latitude) < fabs(latDelta) && fabs(tempPlacemark.coordinate.longitude - longitude) < fabs(lonDelta) {
found = true
}
}
if !found {
shopsToShow.append(checkingLocation)
arrAnnotationsNew.append(checkingLocation)
}
}
// Clean the map
for item: MKAnnotation in self.mapRedes.annotations {
myMap.removeAnnotation(item)
}
// Add new annotations to the map
for item: MKAnnotation in arrAnnotationsNew {
myMap.addAnnotation(item)
}
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// This validation should be added, because it will find all the annotations before the map resize
if CanUpdateMap == true {
LoadMap(mapView)
}
}
Just a correction to my code:
//....
func LoadMap(mapView: MKMapView) {
//....
// Show in the map only the annotations from that specific region
iphoneScaleFactorLatitude = Double(mapView.bounds.size.width / 30) // 30 = width of the annotation
iphoneScaleFactorLongitude = Double(mapView.bounds.size.height / 30) // 30 = height of the annotation
//....
}
func filterAnnotations(mapView: MKMapView, arrAnnotations: [MKAnnotation]) {
let latDelta: Double = mapView.region.span.longitudeDelta / iphoneScaleFactorLatitude
let lonDelta: Double = mapView.region.span.longitudeDelta / iphoneScaleFactorLongitude
//....
}
//....

Resources