Local variable won't copy to global variable - ios

I need to copy a local variable tuple (sortedMarkers) from the function closestMarker() into the global variable sortedMarkerGlobal, however it doesn't seem to be working thus far. I want to use sortedMarkersGlobal in another function but it's count is zero (empty).
class MapViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
var sortedMarkerGlobal: [(lat: String, long: String)] = []
var nextParkCount: Int = 1 // Used to iterate over sortedMarkers with nextPark IBAction button
let locationManager = CLLocationManager()
var lastLocation: CLLocation? = nil // Create internal value to store location from didUpdateLocation to use in func showDirection()
var isAnimating: Bool = false
var dropDownViewIsDisplayed: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
....
}
func closestMarker(userLat: Double, userLong: Double)-> [(lat: String, long: String)] {
/*
var lengthRow = firstRow.count
while (sortedMarkers.count != lengthRow) { // There are markers that haven't been added to sortedMarkers
// Haversine formula
// add nearest marker to sortedMarkers
// Remove recently added marker from ParseViewControler() type tuple
// Recurse until all markers are in sortedMarkers
}
*/
let markerToTest = ParseViewController()
let firstRow = markerToTest.returnParse()
let lat2 = degreesToRadians(userLat) // Nearest lat in radians
let long2 = degreesToRadians(userLong) // Nearest long in radians
let R = (6371).doubleValue // Radius of earth in km
var closestLat: Double? = nil // Used to store the latitude of the closest marker
var closestLong: Double? = nil // Used to store the longitude of the closest marker
//
var indexToDelete: Int = 0 // Used to delete the marker which has been added to sortedMarkers tuple
let lengthRow = firstRow.count
var latLongs: (String, String)
var sortedMarkers: [(lat: String, long: String)] = []
var dynamicRow = firstRow // Tuple that has markers deleted from it
while (sortedMarkers.count != lengthRow) { // Store markers from closest to furtherst distance from user
var shortestDistance = 100.00E100
for (var i = 0; i < dynamicRow.count; i++) {
let testMarker = dynamicRow[i]
let testLat = (testMarker.lat as NSString)
let testLong = (testMarker.long as NSString)
let doubleLat = Double(testLat as String)
let doubleLong = Double(testLong as String)
let lat1 = degreesToRadians(doubleLat!)
let long1 = degreesToRadians(doubleLong!)
let dLat = lat2 - lat1
let dLong = long2 - long1
let a = ((sin((dLat)/2)) * (sin((dLat)/2))) + (cos(lat1)*cos(lat2)*((sin((dLong)/2)) * (sin((dLong)/2))))
let b = sqrt(a)
let d = (2*R) * (asin(b)) // Haversine formula
if (d < shortestDistance) {
closestLat = (doubleLat)
closestLong = (doubleLong)
shortestDistance = d
indexToDelete = i// Keep updating index of closest marker to later be removed
}
}
latLongs = (String(closestLat!), String(closestLong!)) // Each time for loop will find closest marker ~ NOT WORKING ~
sortedMarkers.append(latLongs)
dynamicRow.removeAtIndex(indexToDelete) // Remove marker that has just been added
}
sortedMarkerGlobal = sortedMarkers
return sortedMarkers
}

Related

How do I include location data using AVCapturePhoto?

I'm trying to write an iOS app in Swift that will let the user take a photo, and then I'm going to overlay some additional data onto the image. I would like the images to include location data. I'm using AVCapturePhoto, and I can see in the documentation that it has some metadata variables, but I can't find any info on how to use them. When I take a photo now with my app, it has no location data in the EXIF info.
How can I set the capture sessions to embed the location data?
I haven't dealt with the new IOS 11 AVCapturePhoto object so this answer has to make a couple of assumptions about how to access data but in theory all of this should work.
Before adding location data you need to ask the user if you can use their location. Add the "Privacy - Location When In Use" tag to your Info.plist. Then add the following code somewhere in your initialisation.
// Request authorization for location manager
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
locationManagerStatus = CLLocationManager.authorizationStatus()
default:
locationManagerStatus = .denied
}
Now the following will create a dictionary with location information in it.
// create GPS metadata properties
func createLocationMetadata() -> NSMutableDictionary? {
guard CLLocationManager.authorizationStatus() == .authorizedWhenInUse else {return nil}
if let location = locationManager.location {
let gpsDictionary = NSMutableDictionary()
var latitude = location.coordinate.latitude
var longitude = location.coordinate.longitude
var altitude = location.altitude
var latitudeRef = "N"
var longitudeRef = "E"
var altitudeRef = 0
if latitude < 0.0 {
latitude = -latitude
latitudeRef = "S"
}
if longitude < 0.0 {
longitude = -longitude
longitudeRef = "W"
}
if altitude < 0.0 {
altitude = -altitude
altitudeRef = 1
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy:MM:dd"
gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
formatter.dateFormat = "HH:mm:ss"
gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
gpsDictionary[kCGImagePropertyGPSAltitude] = altitude
if let heading = locationManager.heading {
gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
}
return gpsDictionary;
}
return nil
}
This is where I have to do some guessing as I haven't dealt with IOS 11 AVPhotoCapture. You will need a file data representation of your image data. I assume
AVCapturePhoto.fileDataRepresentation()
returns this. Also you will need the original file metadata. I'll take a guess that
AVCapturePhoto.metadata
contains this. With those assumptions the following function will give you a file data representation with additional location data. There may be newer IOS 11 methods to do this in a cleaner way.
func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
// get image metadata
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
// create new file representation with edited metadata
return photo.fileDataRepresentation(withReplacementMetadata:properties,
replacementEmbeddedThumbnailPhotoFormat:photo.embeddedThumbnailPhotoFormat,
replacementEmbeddedThumbnailPixelBuffer:photo.previewPixelBuffer,
replacementDepthData:photo.depthData)
}
#adamfowlerphoto gave by far the best answer I could find on this subject!
AVCapturePhoto.fileDataRepresentation(withReplacementMetadata:replacementEmbeddedThumbnailPhotoFormat:replacementEmbeddedThumbnailPixelBuffer:replacementDepthData:)
was deprecated in iOS 12. For the new API you must implement the AVCapturePhotoFileDataRepresentationCustomizerProtocol with the method replacementMetadata(). Here is the updated code:
extension CameraViewController: AVCapturePhotoFileDataRepresentationCustomizer {
// create GPS metadata properties
func createLocationMetadata() -> NSMutableDictionary? {
if let location = locationManager.location {
let gpsDictionary = NSMutableDictionary()
var latitude = location.coordinate.latitude
var longitude = location.coordinate.longitude
var altitude = location.altitude
var latitudeRef = "N"
var longitudeRef = "E"
var altitudeRef = 0
if latitude < 0.0 {
latitude = -latitude
latitudeRef = "S"
}
if longitude < 0.0 {
longitude = -longitude
longitudeRef = "W"
}
if altitude < 0.0 {
altitude = -altitude
altitudeRef = 1
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy:MM:dd"
gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
formatter.dateFormat = "HH:mm:ss"
gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
gpsDictionary[kCGImagePropertyGPSAltitude] = altitude
if let heading = locationManager.heading {
gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
}
return gpsDictionary;
}
return nil
}
func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
// get image metadata
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
// create new file representation with edited metadata
return photo.fileDataRepresentation(with: self) ?? Data()
}
func replacementMetadata(for photo: AVCapturePhoto) -> [String : Any]? {
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
return properties
}
}
To print the result you could use this:
static func printEXIFData(imageData: Data) {
var exifData: CFDictionary? = nil
imageData.withUnsafeBytes {
let bytes = $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
if let cfData = CFDataCreate(kCFAllocatorDefault, bytes, imageData.count),
let source = CGImageSourceCreateWithData(cfData, nil) {
exifData = CGImageSourceCopyPropertiesAtIndex(source, 0, nil)
print(exifData)
}
}
}
Don't try to convert your imageData into JPEG representation because it will discard the GPS metadata. (Source: https://stackoverflow.com/a/10339278/647644)
let data = jpegData(compressionQuality: 1.0) // NOOOOO

Convert optional string to double in Swift 3

I have a option string and want to convert that to double.
this worked in Swift 2 , but since converted to Swift 3, I am getting value of 0.
var dLati = 0.0
dLati = (latitude as NSString).doubleValue
I have check and latitude has a optional string value of something like -80.234543218675654 , but dLati value is 0
*************** ok, new update for clarity *****************
I have a viewcontroller which i have a button in it, and when the button is touched, it will call another viewcontroller and pass a few values to it
here is the code for the first viewcontroller
var currentLatitude: String? = ""
var currentLongitude: String? = ""
var deviceName = ""
var address = ""
// somewhere in the code, currentLatitude and currentLongitude are get set
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "map" {
let destViewController : MapViewController = segue.destination as! MapViewController
print(currentLongitude!) // Print display: Optional(-80.192279355363768)
print(currentLatitude!) // Print display: Optional(25.55692663937162)
destViewController.longitude = currentLongitude!
destViewController.latitude = currentLatitude!
destViewController.deviceName = deviceName
destViewController.address = address
}
}
Here is the code for the second view controller called MapViewController
var longitude: String? = " "
var latitude: String? = ""
.
.
override func viewDidLoad() {
if let lat = latitude {
print(lat) // Print display: optiona(25.55692663937162)
dLati = (lat as NSString).doubleValue
print(dLati) // Print display: 0.0
}
.
.
}
Thanks
Borna
A safe way to achieve this without needing to use Foundation types is using Double's initializer:
if let lat = latitude, let doubleLat = Double(lat) {
print(doubleLat) // doubleLat is of type Double now
}
Unwrap the latitude value safely and then use
var dLati = 0.0
if let lat = latitude {
dLati = (lat as NSString).doubleValue
}
let dLati = Double(latitude ?? "") ?? 0.0
This code works fine.
var dLati = 0.0
let latitude: String? = "-80.234543218675654"
if let strLat = latitude {
dLati = Double(strLat)!
}
You can do this simply in one line.
var latitude: Double = Double("-80.234543218675654") ?? 0.0
This creates a variable named latitude that is of type Double that is either instantiated with a successful Double from String or is given a fallback value of 0.0
When you get a string with double value something like this
"Optional(12.34567)"
You can use a Regex which takes out the double value from the string.
This is the example code for a Regex if the string is "Optional(12.34567)":
let doubleLatitude = location.latitude?.replacingOccurrences(of: "[^\\.\\d+]", with: "", options: [.regularExpression])
Actually the word optional was part of the string. Not sure how it got added in the string? But the way I fixed it was like this. latitude was this string "Optional(26.33691567239162)" then I did this code
let start = latitude.index(latitude.startIndex, offsetBy: 9)
let end = latitude.index(latitude.endIndex, offsetBy: -1)
let range = start..<end
latitude = latitude.substring(with: range)
and got this as the final value
26.33691567239162
DonĀ“t convert it to an NSString, you can force it to a Double but have a fallback if it fails. Something like this:
let aLat: String? = "11.123456"
let bLat: String? = "11"
let cLat: String? = nil
let a = Double(aLat!) ?? 0.0 // 11.123456
let b = Double(bLat!) ?? 0.0 // 11
let c = Double(cLat!) ?? 0.0 // 0
So in your case:
dLati = Double(latitude!) ?? 0.0
Update:
To handle nil values do the following (note that let cLat is nil:
// Will succeed
if let a = aLat, let aD = Double(aLat!) {
print(aD)
}
else {
print("failed")
}
// Will succeed
if let b = bLat, let bD = Double(bLat!) {
print(bD)
}
else {
print("failed")
}
// Will fail
if let c = cLat, let cD = Double(cLat!) {
print(cD)
}
else {
print("failed")
}
In swift 3.1, we can combine extensions and Concrete Constrained Extensions
extension Optional where Wrapped == String
{
var asDouble: Double
{
return NSString(string: self ?? "").doubleValue
}
}
Or
extension Optional where Wrapped == String
{
var asDouble: Double
{
return Double(str ?? "0.00") ?? 0.0
}
}
Swift 4
let YourStringValue1st = "33.733322342342" //The value is now in string
let YourStringValue2nd = "73.449384384334" //The value is now in string
//MARK:- For Testing two Parameters
if let templatitude = (YourStringValue1st as? String), let templongitude = (YourStringValue2nd as? String)
{
movetosaidlocation(latitude: Double(templat)!, longitude: Double(templong)!, vformap: cell.vformap)
}
let YourStringValue = "33.733322342342" //The value is now in string
//MARK:- For Testing One Value
if let tempLat = (YourStringValue as? String)
{
let doublevlue = Double(tempLat)
//The Value is now in double (doublevlue)
}

Local variable won't copy to global variable (swift)

I need to copy a local variable tuple (sortedMarkers) from the function closestMarker() into the global variable sortedMarkerGlobal, however it doesn't seem to be working thus far. I want to use sortedMarkersGlobal in another function but it's count is zero (empty).
class MapViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
var sortedMarkerGlobal: [(lat: String, long: String)] = []
var nextParkCount: Int = 1 // Used to iterate over sortedMarkers with nextPark IBAction button
let locationManager = CLLocationManager()
var lastLocation: CLLocation? = nil // Create internal value to store location from didUpdateLocation to use in func showDirection()
var isAnimating: Bool = false
var dropDownViewIsDisplayed: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
....
}
func closestMarker(userLat: Double, userLong: Double)-> [(lat: String, long: String)] {
/*
var lengthRow = firstRow.count
while (sortedMarkers.count != lengthRow) { // There are markers that haven't been added to sortedMarkers
// Haversine formula
// add nearest marker to sortedMarkers
// Remove recently added marker from ParseViewControler() type tuple
// Recurse until all markers are in sortedMarkers
}
*/
let markerToTest = ParseViewController()
let firstRow = markerToTest.returnParse()
let lat2 = degreesToRadians(userLat) // Nearest lat in radians
let long2 = degreesToRadians(userLong) // Nearest long in radians
let R = (6371).doubleValue // Radius of earth in km
var closestLat: Double? = nil // Used to store the latitude of the closest marker
var closestLong: Double? = nil // Used to store the longitude of the closest marker
//
var indexToDelete: Int = 0 // Used to delete the marker which has been added to sortedMarkers tuple
let lengthRow = firstRow.count
var latLongs: (String, String)
var sortedMarkers: [(lat: String, long: String)] = []
var dynamicRow = firstRow // Tuple that has markers deleted from it
while (sortedMarkers.count != lengthRow) { // Store markers from closest to furtherst distance from user
var shortestDistance = 100.00E100
for (var i = 0; i < dynamicRow.count; i++) {
let testMarker = dynamicRow[i]
let testLat = (testMarker.lat as NSString)
let testLong = (testMarker.long as NSString)
let doubleLat = Double(testLat as String)
let doubleLong = Double(testLong as String)
let lat1 = degreesToRadians(doubleLat!)
let long1 = degreesToRadians(doubleLong!)
let dLat = lat2 - lat1
let dLong = long2 - long1
let a = ((sin((dLat)/2)) * (sin((dLat)/2))) + (cos(lat1)*cos(lat2)*((sin((dLong)/2)) * (sin((dLong)/2))))
let b = sqrt(a)
let d = (2*R) * (asin(b)) // Haversine formula
if (d < shortestDistance) {
closestLat = (doubleLat)
closestLong = (doubleLong)
shortestDistance = d
indexToDelete = i// Keep updating index of closest marker to later be removed
}
}
latLongs = (String(closestLat!), String(closestLong!)) // Each time for loop will find closest marker ~ NOT WORKING ~
sortedMarkers.append(latLongs)
dynamicRow.removeAtIndex(indexToDelete) // Remove marker that has just been added
}
sortedMarkerGlobal = sortedMarkers
return sortedMarkers
}
sortedMarkerGlobal is not a global variable. currently it is an instance variable like any of the other variables you have defined (locationManager, lastLocation, isAnimating, etc).
if you want it to be a global variable you should define it outside of your class MapViewController.
another option would be to make it a class variable of MapViewController by defining it as:
class MapViewController: UIViewController {
static var sortedMarkerGlobal: [(lat: String, long: String)] = []
}
Well I tried to test your code in playground and it worked for me
import UIKit
class Test {
var global:[(lat: String, long: String)] = []
func test1() {
var sortedMarkers: [(lat: String, long: String)] = []
var latLongs: (String, String)
latLongs = ("123", "123")
sortedMarkers.append(latLongs)
global = sortedMarkers
}
}
let test:Test = Test()
test.test1();
print("global \(test.global)")
And the result is global [("123", "123")] so I guess it should also work for you, the problem might be somewhere else.
I would suggest that you just use a closures instead of saving to a global variable. This lets you pass in the parameters and return your result based on that. You can save that result in the function that called it and do what ever you want from 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
//....
}
//....

MKPinAnnotation to Detailed view

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

Resources