I have created a set of "IssueLocation"'s, a class which conforms to the MKAnnotation protocol. Why are my annotations (built from API JSON data) not appearing in my map view ?
Here is the code behind it all:
Class code:
class IssueLocation: NSObject, MKAnnotation {
var locationName: String
var campusName: String
var latitude: Double
var longitude: Double
var coordinate: CLLocationCoordinate2D
init(locationName: String, campusName: String, latitude: Double, longitude: Double, coordinate: CLLocationCoordinate2D) {
self.locationName = locationName
self.campusName = campusName
self.latitude = latitude
self.longitude = longitude
self.coordinate = coordinate
super.init()
}
var subtitle: String? {
if (latitude == 44.22438242146097) {
return "West Campus"
} else {
return "Main Campus"
}
}
var title: String? {
return locationName
}
func mapItem() -> MKMapItem {
let addressDictionary = [String(kABPersonAddressStreetKey): locationName]
let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDictionary)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = locationName
return mapItem
}
}
Creating a set of IssueLocations:
func populateMapObjects() {
if populatingMapObjects {
return
}
populatingMapObjects = true
self.loadingIndicator.startAnimating()
var index = 0
Alamofire.request(GWNetworking.Router.MapObjects).responseJSON() { response in
if let JSON = response.result.value {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) {
/* Making an array of all the node IDs from the JSON file */
if (JSON .isKindOfClass(NSArray)) {
for _ in JSON as! [Dictionary<String,AnyObject>] {
if let issueLocation: IssueLocation = IssueLocation(locationName: "Center of the universe", campusName: "Queen's University", latitude: 44.22661586877309, longitude: -76.49380087852478, coordinate: CLLocationCoordinate2D(latitude: 44.22661586877309, longitude: -76.49380087852478)) {
if let locationName = JSON[index]["name"] as? String {
issueLocation.locationName = locationName
}
if let latitude = JSON[index]["coordinates"]!![1] as? Double {
issueLocation.latitude = latitude
}
if let longitude = JSON[index]["coordinates"]!![0] as? Double {
issueLocation.longitude = longitude
}
issueLocation.coordinate = CLLocationCoordinate2D(latitude: issueLocation.latitude, longitude: issueLocation.longitude)
index = index+1
self.mapObjects.append(issueLocation)
}
}
}
dispatch_async(dispatch_get_main_queue()) {
self.loadingIndicator.stopAnimating()
self.populatingMapObjects = false
print(self.mapObjects.count)
}
}
}
}
}
And finally, this is where I try to add the annotations to the map view:
func loadObjectsIntoMapView() {
for mapObject in mapObjects {
let temporaryMapAnnotation = IssueLocation(locationName: mapObject.locationName, campusName: "Main Campus", latitude: mapObject.latitude, longitude: mapObject.longitude, coordinate: mapObject.coordinate)
if (temporaryMapAnnotation.longitude < -76.50921821594238) {
temporaryMapAnnotation.campusName = "West Campus"
}
self.mapView.addAnnotation(temporaryMapAnnotation)
}
}
Move loadObjectsIntoMapView() into this block:
dispatch_async(dispatch_get_main_queue()) {
self.loadingIndicator.stopAnimating()
self.populatingMapObjects = false
print(self.mapObjects.count)
}
Calling loadObjectsIntoMapView() in viewDidLoad will have it execute immediately and the data has not come down from the server yet so you will have no mapObject in mapObjects hence no annotations.
Related
I am struggling with a function which I have made to take the selected annotation (didSelect view:), check the coordinate against all annotation coordinates in the database, and return the uid of the matching annotation.
However, I think I am making a mistake with my for loop, as it is not returning the value for use in the didSelect function. The searchForEvent function is called in didSelect view:, and checks the lat and long of the selected annotation against the database.
Heres the code:
func searchForEvent(latitude: CLLocationDegrees, longitude: CLLocationDegrees) -> String? {
var eventCoordinate: CLLocationCoordinate2D?
var eventKey: String?
var selectedEventKey = ""
DataService.instance.REF_EVENTS.observeSingleEvent(of: .value, with: { (snapshot) in
print("1")
if let eventSnapshot = snapshot.children.allObjects as? [DataSnapshot] {
for event in eventSnapshot {
eventKey = event.key
print("\(eventKey)")
print("2")
if event.childSnapshot(forPath: "coordinate").value != nil {
if let eventDict = event.value as? Dictionary<String, AnyObject> {
print("3")
//pull out value of key coordinate
let coordinateArray = eventDict["coordinate"] as! NSArray
print(coordinateArray)
eventCoordinate = CLLocationCoordinate2D(latitude: coordinateArray[0] as! CLLocationDegrees, longitude: coordinateArray[1] as! CLLocationDegrees)
print(eventCoordinate)
if (eventCoordinate?.latitude, eventCoordinate?.longitude) == (latitude, longitude) {
selectedEventKey = eventKey!
print("\(selectedEventKey), correct event")
} else {
print("incorrect event")
}
}
}
}
}
})
return selectedEventKey
// if selectedEventKey != nil {
// print("4")
// print("\(selectedEventKey)")
// return selectedEventKey
// } else {
// print("empty key")
// return nil
// }
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
print("selected")
self.selectedAnnotation = view.annotation
print(selectedAnnotation?.coordinate.latitude as Any)
print(selectedAnnotation?.coordinate.longitude as Any)
let lat = selectedAnnotation?.coordinate.latitude
let lon = selectedAnnotation?.coordinate.longitude
selectedAnnotationKey = searchForEvent(latitude: lat!, longitude: lon!)
if selectedAnnotationKey == nil {
print("no key")
} else {
print("found event final! \(selectedAnnotationKey)")
}
}
The selectedAnnotationKey is always nil in the didSelect function :(
Any help is greatly appreciated!
EDIT
Here is the updated function, thank you Sh_Khan for your help with it. While it is printing the right value in the debug area, it continues looping through the "events" in the database and returns nil at the end after finishing.
func searchForEvent(latitude: CLLocationDegrees, longitude: CLLocationDegrees , completion:#escaping(_ str:String?) -> Void ) {
var eventCoordinate: CLLocationCoordinate2D?
var eventKey: String?
var selectedEventKey = ""
DataService.instance.REF_EVENTS.observeSingleEvent(of: .value, with: { (snapshot) in
print("1")
if let eventSnapshot = snapshot.children.allObjects as? [DataSnapshot] {
for event in eventSnapshot {
eventKey = event.key
print("\(eventKey)")
print("2")
if event.childSnapshot(forPath: "coordinate").value != nil {
if let eventDict = event.value as? Dictionary<String, AnyObject> {
print("3")
//pull out value of key coordinate
let coordinateArray = eventDict["coordinate"] as! NSArray
print(coordinateArray)
eventCoordinate = CLLocationCoordinate2D(latitude: coordinateArray[0] as! CLLocationDegrees, longitude: coordinateArray[1] as! CLLocationDegrees)
print(eventCoordinate)
if (eventCoordinate?.latitude, eventCoordinate?.longitude) == (latitude, longitude) {
selectedEventKey = eventKey!
print("\(selectedEventKey), correct event")
completion(selectedEventKey)
} else {
print("incorrect event")
completion(nil)
}
}
}
}
}
})
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
print("selected")
self.selectedAnnotation = view.annotation
let lat = selectedAnnotation?.coordinate.latitude
let lon = selectedAnnotation?.coordinate.longitude
searchForEvent(latitude: lat!, longitude: lon!) { (str) in
self.selectedAnnotationKey = str
print("here it is \(str)")
print(self.selectedAnnotationKey)
}
}
And the debug printout:
selected
1
Optional("31E2932B-A037-4BB1-B93E-7504B61AC4E7")
2
3
(
"-36.84745654404946",
"174.7760903030886"
)
Optional(__C.CLLocationCoordinate2D(latitude: -36.847456544049464, longitude: 174.77609030308864))
incorrect event
here it is nil
nil
Optional("71173419-7E08-415C-9236-B1C8495A6BA9")
2
3
(
"-36.86687593953122",
"174.7585811441448"
)
Optional(__C.CLLocationCoordinate2D(latitude: -36.866875939531219, longitude: 174.75858114414478))
Below it finds the correct event, both str and self.selectedAnnotationKey are correct, but then keeps going and overwrites it!!!
71173419-7E08-415C-9236-B1C8495A6BA9, correct event
here it is Optional("71173419-7E08-415C-9236-B1C8495A6BA9")
Optional("71173419-7E08-415C-9236-B1C8495A6BA9")
Optional("7AC6429E-74B6-4A4E-A638-53981ACBFFBA")
2
3
(
"-36.2429468",
"175.3981152"
)
Optional(__C.CLLocationCoordinate2D(latitude: -36.242946799999999, longitude: 175.39811520000001))
incorrect event
here it is nil
nil
You need a completion
func searchForEvent(latitude: CLLocationDegrees, longitude: CLLocationDegrees , completion:#escaping(_ str:String?) -> Void ) {
var eventCoordinate: CLLocationCoordinate2D?
var eventKey: String?
var selectedEventKey = ""
DataService.instance.REF_EVENTS.observeSingleEvent(of: .value, with: { (snapshot) in
print("1")
if let eventSnapshot = snapshot.children.allObjects as? [DataSnapshot] {
for event in eventSnapshot {
eventKey = event.key
print("\(eventKey)")
print("2")
if event.childSnapshot(forPath: "coordinate").value != nil {
if let eventDict = event.value as? Dictionary<String, AnyObject> {
print("3")
//pull out value of key coordinate
let coordinateArray = eventDict["coordinate"] as! NSArray
print(coordinateArray)
eventCoordinate = CLLocationCoordinate2D(latitude: coordinateArray[0] as! CLLocationDegrees, longitude: coordinateArray[1] as! CLLocationDegrees)
print(eventCoordinate)
if (eventCoordinate?.latitude, eventCoordinate?.longitude) == (latitude, longitude) {
selectedEventKey = eventKey!
print("\(selectedEventKey), correct event")
completion(selectedEventKey)
} else {
print("incorrect event")
completion(nil)
}
}
}
}
}
})
}
//
To call
searchForEvent(//value1,//value2) { (str) in
print(str)
}
You need a completion handler for this
I have a model class where I parse my item from a JSON file:
class Venue: NSObject, GMUClusterItem {
let name: String?
let locationName: String?
let position: CLLocationCoordinate2D
let image = GMSMarker()
init(name: String, locationName: String?, position: CLLocationCoordinate2D, image: GMSMarker)
{
self.name = name
self.locationName = locationName
self.position = position
//self.image = image
super.init()
}
var subtitle: String? {
return locationName
}
class func from(json: JSON) -> Venue?
{
var name: String
if let unwrappedTitle = json["name"].string {
name = unwrappedTitle
} else {
name = ""
}
let locationName = json["location"]["address"].string
let lat = json["location"]["lat"].doubleValue
let long = json["location"]["lng"].doubleValue
let position = CLLocationCoordinate2D(latitude: lat, longitude: long)
let image = GMSMarker()
return Venue(name: name, locationName: locationName, position: position, image: image)
}
}
And here, after I fetch the data, I want to bring the marker in the mapView and I want to customize it with an image.
var venues = [Venue]()
private func generateClusterItems() {
for venue in venues {
let name = venue.name
let position = venue.position
let locationName = venue.locationName
let image = GMSMarker()
let item = Venue(name: name!, locationName: locationName, position: position, image: image)
let markerView = UIImage(named: "K_Annotation.png")!
image.icon = markerView
clusterManager.add(item)
}
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
But it doesn't work. It brings me the default marker of google maps. I don't know what I do wrong?
You are changing the image of the image variable not the item.image variable, so when you add the item the image is never being added to it.
private func generateClusterItems() {
for venue in venues {
let name = venue.name
let position = venue.position
let locationName = venue.locationName
let image = GMSMarker()
let item = Venue(name: name!, locationName: locationName, position: position, image: image)
let markerView = UIImage(named: "K_Annotation.png")!
// Change to this
item.image.icon = markerView
clusterManager.add(item)
}
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
Solution found:Here starts the class in GMUDefaultClusterRenderer.m at clustering folder
- (GMSMarker *)markerWithPosition:(CLLocationCoordinate2D)position
from:(CLLocationCoordinate2D)from
userData:(id)userData
clusterIcon:(UIImage *)clusterIcon
animated:(BOOL)animated {.....
......
I replace the original with this:
if (clusterIcon != nil) {
marker.icon = clusterIcon;
marker.groundAnchor = CGPointMake(0.5, 0.5);
}else{
marker.icon = [UIImage imageNamed:#"K_Annotation.png"];
}
I'm having an issue with updating/refreshing the map.
The code below is from the MainViewController
let forecastConstants = ForecastConstants(apiKey: forecastAPIKey)
coordinates.latitude = latPassed
coordinates.longitude = LongPassed
if latPassed == 0 && LongPassed == 0 {
coordinates.latitude = 43.161030
coordinates.longitude = -77.610922
}
LatPassed and LongPassed are passed from another controller (LocationViewController), where a new location (Lat and Long) is entered. All Controllers are in embedded in TabBar.
In MapViewController, I have:
var coordinate = MainForecastViewController()
override func viewDidLoad() {
super.viewDidLoad()
let lat = coordinate.coordinates.latitude
let lon = coordinate.coordinates.longitude
}
mapView.delegate = self
let coord = CLLocationCoordinate2D(latitude: lat, longitude: lon)
let region = MKCoordinateRegionMakeWithDistance(coord, 1000, 1000)
mapView.setRegion(region, animated: true)
pin = AnnotationPin(title: "Load temp here", subtitle: "", coordinate: coord)
mapView.addAnnotation(pin)
mapView.selectAnnotation(pin, animated: true)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKAnnotationView(annotation: pin, reuseIdentifier: "forecast")
annotationView.image = UIImage(named: "weatherMapIcon")
let transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
annotationView.transform = transform
return annotationView
}
The problem is, when I enter a new location, it is updated in the main controller, but when I switch to MapViewController, the pin is still pinned to the default lat and long (43.161030, -77.610922). Am I doing this right and just need to update/refresh the mapView/Pin, or is there another approach to this?
Thanks you advance!
UPDATE:
Here is the MainViewController:
class MainViewController: UIViewController {
var latPassed = Double()
var LongPassed = Double()
// These are passed from LocationViewController
var coordinates: (latitude: Double, longitude: Double) = (43.161030,-77.610922)
// These are the default coordinates
func loadWeatherTemp() {
let forecastConstants = ForecastConstants(apiKey: forecastAPIKey)
coordinates.latitude = latPassed
coordinates.longitude = LongPassed
if latPassed == 0 && LongPassed == 0 {
coordinates.latitude = 43.161030
coordinates.longitude = -77.610922
}
forecastConstants.getForecast(latitude: coordinates.latitude, longitude: coordinates.longitude) { (currentWeather) in
if let currentWeather = currentWeather {
DispatchQueue.main.async {
// stuff
}
}
}
// more stuff
}
I'd prefer to have a coordinate object to have in MapViewController() and set the new coordinates from MainViewController(). I'm telling Something like below:
MapViewController.swift
var newCoordinates = CLLocationCoordinate2D() {
didSet {
let region = MKCoordinateRegionMakeWithDistance(newCoordinates, 1000, 1000)
mapView.setRegion(region, animated: true)
pin = AnnotationPin(title: "Load temp here", subtitle: "", coordinate: coord)
mapView.addAnnotation(pin)
mapView.selectAnnotation(pin, animated: true)
}
UPDATE: -
MainViewController.swift
class MainViewController: UIViewController {
var latPassed = Double()
var LongPassed = Double()
weak var mapVC = MapViewController()//this will be your mapvc which is loaded in your tabbar.
var coordinates: (latitude: Double, longitude: Double) = (43.161030,-77.610922) {
didSet {
mapVC.newCoordinates = self.coordinates // Every time you update your coordinates here, send that coordinate to MapViewController() so that it stays same.
}
}
func loadWeatherTemp() {
let forecastConstants = ForecastConstants(apiKey: forecastAPIKey)
coordinates.latitude = latPassed
coordinates.longitude = LongPassed
if latPassed == 0 && LongPassed == 0 {
coordinates.latitude = 43.161030
coordinates.longitude = -77.610922
}
forecastConstants.getForecast(latitude: coordinates.latitude, longitude: coordinates.longitude) { (currentWeather) in
if let currentWeather = currentWeather {
DispatchQueue.main.async {
// stuff
}
}
}
}
Sorry for late reply.
Hope it helps!
My app currently has three tabs, a tab for pinning a location, and a detailView of the pinned location on the second tab. I am trying to save the location of the pin into NSUserdefaults. I would then like that location to stay pinned upon reloading the app, and therefore the detail view would still display the detail view of the pinned location. Here is what I have so far,
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
// Find location of user
var userLocation:CLLocation = locations[0] as! CLLocation
var latitude = userLocation.coordinate.latitude
var longitude = userLocation.coordinate.longitude
var latDelta:CLLocationDegrees = 0.01
var longDelta: CLLocationDegrees = 0.01
var span: MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
var location:MKUserLocation = currentLocation;
var region: MKCoordinateRegion = MKCoordinateRegionMake(location.coordinate, span)
var coordinate:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude);
carInitialLocation = userLocation;
let locationData = NSKeyedArchiver.archivedDataWithRootObject(carInitialLocation);
NSUserDefaults.standardUserDefaults().setObject(locationData, forKey: "locationData");
carInitialCoordinate = coordinate;
self.map.setRegion(region, animated: true);
}
override func viewDidLoad() {
super.viewDidLoad()
if let loadedData = NSUserDefaults.standardUserDefaults().dataForKey("locationData") {
if let loadedLocation = NSKeyedUnarchiver.unarchiveObjectWithData(loadedData) as? CLLocation {
println(loadedLocation.coordinate.latitude);
println(loadedLocation.coordinate.longitude);
var annotation = MKPointAnnotation()
annotation.coordinate = loadedLocation.coordinate
annotation.title = title
self.map.addAnnotation(annotation)
}
}
map.addAnnotations(artworks)
map.delegate = self;
manager.delegate = self;
manager.desiredAccuracy = kCLLocationAccuracyBest;
manager.requestWhenInUseAuthorization();
manager.startUpdatingLocation();
self.map.showsUserLocation = true;
currentLocation = map.userLocation;
}
I then want the pinLocation button to be deactivated once the user has pinned a location once. I try to do this as so:
#IBAction func pinLocationButton(sender: AnyObject) {
// add location to the array, so it can be retrieved and put it into temporary storage
//places.append(["name":title,"lat":"\(newCoordinate.latitude)","lon":"\(newCoordinate.longitude)"])
if let loadedData = NSUserDefaults.standardUserDefaults().dataForKey("locationData") {
if let loadedLocation = NSKeyedUnarchiver.unarchiveObjectWithData(loadedData) as? CLLocation {
println(loadedLocation.coordinate.latitude);
println(loadedLocation.coordinate.longitude);
pinLocationButton.enabled = false;
}
}
var location = carInitialLocation
var coordinate = carInitialCoordinate
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: { (placemarks, error) -> Void in
var title = ""
if (error == nil) {
if let p = CLPlacemark(placemark: placemarks?[0] as! CLPlacemark) {
var subThoroughfare:String = ""
var thoroughfare:String = ""
if p.subThoroughfare != nil {
subThoroughfare = p.subThoroughfare
}
if p.thoroughfare != nil {
thoroughfare = p.thoroughfare
}
completeAddress = self.displayLocationInfo(p);
title = "\(subThoroughfare) \(thoroughfare)"
}
}
// annotation, i.e pins
var annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = title
self.map.addAnnotation(annotation)
// NSUserDefaults.standardUserDefaults().setObject(places, forKey: "places")
})
}
Then, in my detailVC I attempt to reverse geocode the pinned location, but it is defaulting to the current location.. which I don't understand why
Here's the code:
super.viewDidLoad()
addressLabel.font = UIFont(name: addressLabel.font.fontName, size: 18)
smallMapView.delegate = self;
locationManager.delegate = self;
smallMapView.mapType = MKMapType.Hybrid;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.requestWhenInUseAuthorization();
locationManager.startUpdatingLocation();
smallMapView.zoomEnabled = true;
smallMapView.rotateEnabled = true;
if let loadedData = NSUserDefaults.standardUserDefaults().dataForKey("locationData") {
if let loadedLocation = NSKeyedUnarchiver.unarchiveObjectWithData(loadedData) as? CLLocation {
println(loadedLocation.coordinate.latitude);
println(loadedLocation.coordinate.longitude);
CLGeocoder().reverseGeocodeLocation(loadedLocation, completionHandler: { (placemarks, error) -> Void in
var title = "";
var subtitle = "";
var locality = "";
if(error == nil) {
if let placemark = CLPlacemark(placemark: placemarks?[0] as! CLPlacemark) {
var subThoroughfare:String = "";
var thoroughfare:String = "";
var locality:String = "";
var postalCode:String = "";
var administrativeArea:String = "";
var country:String = "";
if (placemark.subThoroughfare != nil) {
subThoroughfare = placemark.subThoroughfare;
}
if(placemark.thoroughfare != nil) {
thoroughfare = placemark.thoroughfare;
}
if(placemark.locality != nil) {
locality = placemark.locality;
}
if(placemark.postalCode != nil) {
postalCode = placemark.postalCode;
}
if(placemark.administrativeArea != nil) {
administrativeArea = placemark.administrativeArea;
}
if(placemark.country != nil) {
country = placemark.country;
}
println("viewcontroller placmark data:");
println(locality);
println(postalCode);
println(administrativeArea);
println(country);
title = " \(subThoroughfare) \(thoroughfare) \n \(locality), \(administrativeArea) \n \(postalCode) \(country)";
subtitle = " \(subThoroughfare) \(thoroughfare)";
println(title);
self.addressLabel.text = title;
}
}
var latitude = loadedLocation.coordinate.latitude;
var longitude = loadedLocation.coordinate.longitude;
var latDelta:CLLocationDegrees = 0.001;
var longDelta:CLLocationDegrees = 0.001;
var span: MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta);
var overallLoc = CLLocationCoordinate2DMake(latitude, longitude);
var region:MKCoordinateRegion = MKCoordinateRegionMake(overallLoc, span);
var annotation = MKPointAnnotation();
annotation.coordinate = loadedLocation.coordinate;
annotation.title = subtitle;
self.smallMapView.addAnnotation(annotation);
self.smallMapView.setRegion(region, animated: true)
})
}
}
}
in the pinLocationButton function, you use the manager.location which is wrong, you should use
CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
var title = ""
if (error == nil) {
// ....
I want to update map view every 60 seconds & make an annotation with custom pin/image but want to show standard pin [red color - See Figure 6-1] icon to the previously drawn annotation. I tried like below code but that does not working properly. With another custom image it works but i can't show that default/standard red pin to previous annotation. Please see "currentLocationHasBeenUpdated" function where i tried to show standard red icon to the previous annotation. Please let me know how can i achieve this. Thanks.
My ViewController :
import UIKit
import MapKit
class FirstTabController: UIViewController, MKMapViewDelegate, DelegateForUpdateCurrentLocation {
#IBOutlet weak var mapView: MKMapView!
var gpsLocations : [[String: AnyObject]]!
var dataManager : DataManager!
var currentAnnotation : MKAnnotation!
//Return CLLocation Coordinate
func getLocationObject(latitude:Double, longitude:Double) -> CLLocationCoordinate2D {
return CLLocationCoordinate2D(
latitude: latitude,
longitude: longitude
)
}
//Create annotation object & return
func createAnnotation(latitude:Double, longitude:Double, locationName:String, territory:String) -> MKPointAnnotation {
let annotation = MKPointAnnotation()
annotation.coordinate = self.getLocationObject(latitude, longitude: longitude)
annotation.title = locationName
annotation.subtitle = territory
return annotation
}
//Create annotaion for current ship position
func createAnnotationForCurrentPosition(location:[String: AnyObject]) -> MKPointAnnotation {
let latitude = (location["latitude"] as? Double)!
let longitude = (location["longitude"] as? Double)!
let name = (location["locationName"] as? String)!
let territory = (location["territory"] as? String)!
return self.createAnnotation(latitude, longitude: longitude, locationName: name, territory: territory)
}
//Set region on map view
func setRegion(location:[String: AnyObject]){
let latitude = (location["latitude"] as? Double)!
let longitude = (location["longitude"] as? Double)!
let location = self.getLocationObject(latitude, longitude: longitude)
//Set zoom span
let span = MKCoordinateSpanMake(0.05, 0.05)
//Create region on map view & show
let region = MKCoordinateRegion(center: location, span: span)
self.mapView.setRegion(region, animated: true)
}
//This function will fire from data manager when current location from API has been updated
func currentLocationHasBeenUpdated(location:[String: AnyObject]){
dispatch_async(dispatch_get_main_queue()){
if self.currentAnnotation != nil {
var currentAnnotationView = self.mapView.viewForAnnotation(self.currentAnnotation)
if currentAnnotationView != nil {
currentAnnotationView.image = nil //this line makes app crash [gave nil to show standard red icon]
// currentAnnotationView.image = UIImage(named:"first") - this line works perfectly with different icon. but i don't want any custom icon except the standard red one
}
}
self.mapView.addAnnotation(self.createAnnotationForCurrentPosition(location))
self.setRegion(location)
}
}
//Update map view by scheduling time if current location is changed yet
func updateMapView() {
self.dataManager.getCurrentLocation()
}
override func viewDidLoad() {
super.viewDidLoad()
//Get gps locations from data manager
self.dataManager = DataManager()
self.dataManager.delegate2 = self
self.dataManager.getCurrentLocation()
//self.mapView.userTrackingMode = MKUserTrackingMode.FollowWithHeading
//Get GPS location by scheduling time
NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("updateMapView"), userInfo: nil, repeats: true)
}
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer!{
if overlay is MKPolyline {
var polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.blueColor()
polylineRenderer.lineWidth = 2
return polylineRenderer
}
return nil
}
//Make custom annotaion pin if the annotation is of ship's current position
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!{
println("Hello")
self.currentAnnotation = annotation
//Custom annotation view with custom pin icon
let reuseId = "custom"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
annotationView.canShowCallout = true
}
else {
annotationView.annotation = annotation
}
//Set annotation specific properties after the view is dequeued or created...
annotationView.image = UIImage(named:"pinicon")
return annotationView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
and for coordinates from api
import Foundation
extension String {
func toDouble() -> Double? {
return NSNumberFormatter().numberFromString(self)?.doubleValue
}
}
class DataSet{
var settings : Settings!
var getService : GetService!
var currentLocation : [String: AnyObject]!
//Object initialization for API call
init(){
self.settings = Settings()
self.getService = GetService()
}
func getCurrentLocation(callback:([String: AnyObject]) -> ()){
self.getService.apiCallToGet(self.settings.getCoordinates(), callback: {
(response) in
var location = [String: AnyObject]()
for item in response {
if let dictionary = item as? NSDictionary {
if let lat = dictionary["Lat"] as? String {
if lat.toDouble() != nil {
location["latitude"] = lat.toDouble()!
}
}
if let long = dictionary["Long"] as? String {
if long.toDouble() != nil {
location["longitude"] = long.toDouble()!
}
}
if let name = dictionary["Name"] as? String {
location["locationName"] = name
}
if let territory = dictionary["Territory"] as? String {
location["territory"] = territory
}
println(location["latitude"]!)
println(location["longitude"]!)
if self.currentLocation != nil {
if ((self.currentLocation["latitude"] as? Double) != (location["latitude"] as? Double)) && ((self.currentLocation["longitude"] as? Double) != (location["longitude"] as? Double)){
self.currentLocation = location
callback(location)
}
} else {
self.currentLocation = location
callback(location)
}
}
}
})
}
}
I have solved my problem by creating custom annotation class like below -
import UIKit
import MapKit
var ARROW_ANNOTATION : NSString = "ARROW_ANNOTATION"
var PIN_ANNOTATION : NSString = "PIN_ANNOTATION"
class Annotation: NSObject, MKAnnotation {
var currentLocation: CLLocationCoordinate2D
var _title : String
var subTitle : String
var direction : CLLocationDirection!
var typeOfAnnotation : String!
init(coordinate: CLLocationCoordinate2D, title : String, subTitle : String) {
self.currentLocation = coordinate
self._title = title
self.subTitle = subTitle
}
func getLocation() -> CLLocation {
return CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
}
var coordinate: CLLocationCoordinate2D {
return self.currentLocation
}
var title : String {
return self._title
}
var subtitle : String {
return self.subTitle
}
}
and In my ViewController file -
override func viewDidLoad() {
super.viewDidLoad()
var annotation1 : Annotation = Annotation(coordinate: location, title: name, subTitle: territory)
annotation.typeOfAnnotation = PIN_ANNOTATION as String
self.mapView.addAnnotation(annotation1)
var annotation2 : Annotation = Annotation(coordinate: location, title: name, subTitle: territory)
annotation.typeOfAnnotation = ARROW_ANNOTATION as String
self.mapView.addAnnotation(annotation2)
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!{
let reuseId = "custom"
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
var customAnnotationView : MKAnnotationView!
if let _annotation = annotation as? Annotation {
if _annotation.typeOfAnnotation == PIN_ANNOTATION {
customAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
customAnnotationView.canShowCallout = true
//customAnnotationView.draggable = true
} else {
customAnnotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
// 17 - Determine the direction
let arrowImage = UIImage(named:"arrow")!
let direction = _annotation.direction
customAnnotationView.image = self.rotatedImage(arrowImage, byDegreesFromNorth: direction)
customAnnotationView.canShowCallout = true
}
} else {
return nil
}
return customAnnotationView
}