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!
Related
When I add the annotations to the map they sometimes show and sometimes not depending on how close they are to each other. If they are in the same house lets say one won't show. How do I make both of them show? Do I need to make a custom annotation class? I heard ios11 has a clumping feature, do I need to use that? Here is the code(abridged):
import UIKit
import MapKit
import Firebase
class GameViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
fileprivate var locations = [CLLocation]()
fileprivate var userLocations = [(loc: CLLocation, name: String, team: String)]()
fileprivate var userAnnotations = [MKAnnotation]()
fileprivate var hasBeenUP = false
var ref: FIRDatabaseReference!
let uid = FIRAuth.auth()!.currentUser!.uid
var timer = Timer()
var timeLeft = 0.0
var firstTimer = Timer()
var name = ""
var team = ""
override func viewDidLoad() {
super.viewDidLoad()
let center = CLLocationCoordinate2D(latitude: 47.786769, longitude: -20.413634)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
self.mapView.setRegion(region, animated: true)
mapView.mapType = .hybrid
locationManager.startUpdatingLocation()
ref = FIRDatabase.database().reference()
setupULSending()
getMetaInfo()
ref.child("realtimeLocations").observe(FIRDataEventType.value, with: { (snapshot) in
self.userLocations = []
for rest in snapshot.children.allObjects as! [FIRDataSnapshot] {
guard let snapshotValue = snapshot.value as? NSDictionary, let snapVal = snapshotValue[rest.key] as? NSDictionary else {
break
}
let name = snapVal["name"] as! String
let team = snapVal["team"] as? String ?? ""
if let lat = snapVal["lat"] as? Double,
let long = snapVal["long"] as? Double {
let location = CLLocation(latitude: lat, longitude: long)
self.userLocations.append((loc: location, name: name, team: team))
}else {
}
}
DispatchQueue.main.async {
self.updateUserLocation()
}
})
}
private lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.allowsBackgroundLocationUpdates = true
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
return manager
}()
func updateUserLocation() {
for an in self.mapView.annotations {
mapView.removeAnnotation(an)
}
for loc in userLocations {
let annotation = MKPointAnnotation()
annotation.coordinate = loc.loc.coordinate
annotation.title = loc.name
annotation.subtitle = "local"
mapView.addAnnotation(annotation)
}
}
}
// MARK: - CLLocationManagerDelegate
extension GameViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations
locations: [CLLocation]) {
let location = locations.last as! CLLocation
self.locations.append(location)
}
}
On the MKAnnotationView, you have to set the MKFeatureDisplayPriority to 'required'. You can modify the annotation views by implementing MKMapViewDelegate and mapView(MKMapView, viewFor: MKAnnotation). Something like this:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
var view = mapView.dequeueReusableAnnotationView(withIdentifier: "yourIdentifier")
if view == nil {
view = MKMarkerAnnotationView(annotation: nil, reuseIdentifier: "yourIdentifier")
}
view?.displayPriority = .required
return view
}
More options for this are explained in the WWDC 2017 video 237 "What's New in MapKit"
I'm designing an app which has the a feature to let user add their own pin. Recently, I found my app has a memory leak with the title of "ContiguousArrayStorage". I debugged a little and figured out it was a problem with my for loop in my viewdidload, but I am not sure how to fix the leak.
Here is my code:
#IBOutlet weak var AppleMap: MKMapView!
#IBOutlet weak var LogoutOutlet: UIButton!
#IBOutlet weak var OutletforAddPokemon: UIButton!
#IBOutlet weak var FindLocationOutlet: UIButton!
#IBOutlet weak var FilterOutlet: UIButton!
let locationManager = CLLocationManager()
let calendar: NSCalendar! = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
var increment:Int = 0
var currentLoc:CLLocationCoordinate2D?
var centerMap = true
let StoringPin = FIRDatabase.database().reference().child("locations")
var Date = NSDate()
override func viewDidLoad() {
super.viewDidLoad()
AppleMap.delegate = self
StoringPin.observeEventType(.Value, withBlock: {
snapshot in
if snapshot.value is NSNull {
return
}
let val = snapshot.value as! [String : [String : AnyObject]]
for key in val.keys {
let latitudedata = val[key]!["latitude"] as! Double
let longitudedata = val[key]!["longitude"] as! Double
let namedata = val[key]!["name"] as! String
let Username = val[key]!["Username"]
as! String
let DATE = val[key]!["Date"]
as! String
let NumberOflikesforuser = val[key]!["Likes"] as! Int
let NumberOfDislikesforuser = val[key]!["Dislikes"] as! Int
let coord = CLLocationCoordinate2D(latitude: latitudedata, longitude: longitudedata)
let artwork = Capital(title: "\(namedata)", coordinate: coord, info: "HI", username: Username, NumofLikes: NumberOflikesforuser,NumofDisLikes: NumberOfDislikesforuser, UIDSTring: UID, date: DATE, color: MKPinAnnotationColor.Green)
artwork.subtitle = DATE
print("k")
let permastringforemail:String = Username
print(++self.increment)
print(UID)
print(permastringforemail)
stringforemail = permastringforemail
Arrayforpins.append(artwork)
self.AppleMap.addAnnotation(Arrayforpins[Arrayforpins.count - 1])
for Capital in Arrayforpins {
self.AppleMap.addAnnotation(Capital)
}
}
})
print(LogoutOutlet)
LogoutOutlet.layer.cornerRadius = 4
FindLocationOutlet.layer.cornerRadius = 4
OutletforAddPokemon.layer.cornerRadius = 4
//FilterOutlet.layer.cornerRadius = 4
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
let annotationView = MKAnnotationView()
let detailButton: UIButton = UIButton.init(type: .DetailDisclosure) as UIButton
annotationView.rightCalloutAccessoryView = detailButton
}
let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
AppleMap.setRegion(coordinateRegion, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
AppleMap.showsUserLocation = true
} else {
locationManager.requestWhenInUseAuthorization()
}
print("update")
currentLoc = locValue
if centerMap {
centerMap = false
centerMapOnLocation(CLLocation(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude))
}
}
#IBAction func SendtoSelector(sender: AnyObject) {
self.performSegueWithIdentifier("SeguetoSelector", sender: self)
}
#IBAction func FilterFunc(sender: AnyObject) {
self.performSegueWithIdentifier("SeguetoFilter", sender: self)
}
#IBAction func FindLocation(sender: AnyObject) {
centerMapOnLocation(CLLocation(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude))
}
#IBAction func Logout(sender: AnyObject) {
LO = true
self.performSegueWithIdentifier("BacktoLoginScreen", sender: self)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
locationManager
if IndexBoolean == true{
//let annotationView = MKPinAnnotationView()
print("Hi")
let artwork = Capital(title: "\(pickerDataSource[chosenindex])", coordinate: CLLocationCoordinate2D(latitude: currentLoc!.latitude, longitude: currentLoc!.longitude), info: "HEY", username: stringforemail, NumofLikes: NumberOfLikes, NumofDisLikes: NumberOfDislike, UIDSTring: UID, date: stringfordate2, color: MKPinAnnotationColor.Green)
//print(now)
print(chosenindex)
artwork.title = "\(pickerDataSource[chosenindex])"
//artwork.subtitle = stringfordate
AppleMap.addAnnotation(artwork)
//annotationView.pinColor = artwork.Green
Arrayforpins.append(artwork)
print(stringforemail)
AppleMap.addAnnotation(Arrayforpins[Arrayforpins.count - 1])
for Capital in Arrayforpins{
AppleMap.addAnnotation(Capital)
}
IndexBoolean = false
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "Central Time")
var utcTimeZoneSTR = formatter.stringFromDate(Date)
stringfordate = "\(utcTimeZoneSTR)"
let uid = NSUUID().UUIDString
UID = uid
StoringPin.child(uid).setValue(["name" : pickerDataSource[chosenindex],
"latitude" : currentLoc!.latitude,
"longitude" : currentLoc!.longitude,"Array Position" : chosenindex,"Username": stringforemail, "Likes": NumberOfLikes2, "Dislikes":
NumberOfDislike, "UID": UID, "Date": stringfordate])
if FilterBoolean == true {
print("b")
if FilterDataSource[Intforfilter] != stringforname {
print("k")
//self.AppleMap.viewForAnnotation(artwork)?.hidden = true
FilterBoolean == false
}
//else {
// print("m")
// self.AppleMap.removeAnnotation(artwork)
//
// }
FilterBoolean == false
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "Capital"
print(++increment)
if annotation.isKindOfClass(Capital.self) {
print("CAPITAL")
if let annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier) {
annotationView.annotation = annotation
return annotationView
} else {
let annotationView = MKPinAnnotationView(annotation:annotation, reuseIdentifier:identifier)
annotationView.enabled = true
annotationView.canShowCallout = true
//annotationView.pinColor = MKPinAnnotationColor.Green
let btn = UIButton(type: .DetailDisclosure)
annotationView.rightCalloutAccessoryView = btn
return annotationView
}
}
return nil
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let capital = view.annotation as! Capital
let placeName = capital.title
let placeInfo = capital.info
let UserName = capital.username
stringforname = view.annotation!.title!!
coords = view.annotation!.coordinate
stringforemail = capital.username
stringfordate2 = capital.date
NumberOfLikes2 = capital.NumofLikes
UID = capital.UIDSTring
print(stringforname)
print(UID)
self.performSegueWithIdentifier("SegueToInfo", sender: self)
}
I note you're not taking care to specify how to capture self in your closure. When in a disposable object context (a UIViewController) and an async closure you always have to worry about this. You need to capture it as weak or unowned. Are you familiar with closure capture lists?
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID56
The pattern I use is to capture self weakly and then bail if self has become nil, e.g. the UIViewController has been dismissed and harvested. If it is still around, I temporarily capture it strongly with a local variable in a guard...let statement.
withBlock: { [weak self] (snapshot) in
guard let s = self else {
print("Callback called after view unloaded")
return
}
// now use 's' instead of 'self'
...
}
Can't say this is the only problem because we don't have the declarations for your helper classes. For example we don't know what mischief Capital is up to.
By the way, just be clear that your for loop does not run at viewDidLoad(). You're merely registering a callback that only reacts if and when the .Value event occurs on the StoringPin object.
I've been having trouble figuring out how to pass a custom variable in a map pin to another view controller in Swift. I know that passing the coordinates, title, and subtitle are available when you addAnnotation. I would like to try and pass a custom variable but hidden. Is there such a thing? Below I am getting the users location, mapping it, dropping a pin of a couple locations nearby with annotations which goes to another view controller and passes just the title and subtitle. Any insight is greatly appreciated.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var mappedCity = String()
var mappedState = String()
var manager = CLLocationManager()
var annotation:MKAnnotation!
var error:NSError!
var pointAnnotation:MKPointAnnotation!
var pinAnnotationView:MKPinAnnotationView!
var selectedAnnotation: MKPointAnnotation!
private var mapChangedFromUserInteraction = false
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = true
if CLLocationManager.locationServicesEnabled(){
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0]
let latitude = userLocation.coordinate.latitude
let longitude = userLocation.coordinate.longitude
let latDelta:CLLocationDegrees = 0.05
let lonDelta:CLLocationDegrees = 0.05
let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)
self.mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
CLGeocoder().reverseGeocodeLocation(userLocation) { (placemarks, error) in
if (error != nil){
print(error)
}else {
if let p = placemarks?[0]{
let locality = p.locality ?? ""
let administrativeArea = p.administrativeArea ?? ""
self.mappedCity = String(locality)
self.mappedState = String(administrativeArea)
self.parseJSON("\(locality)", state: "\(administrativeArea)")
}
}
}
self.manager.stopUpdatingLocation()
}
func parseJSON(city: String, state: String){
let passedCity = city
let passedState = state
let escapedCity = passedCity.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let escapedState = passedState.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let url = NSURL(string:"http://www.API.com/api.php?city=\(escapedCity)&stateAbv=\(escapedState)")!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (items, response, error) -> Void in
if error != nil {
print(error)
}else {
if let items = items {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(items, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if jsonResult.count > 0 {
if let datas = jsonResult["data"] as? NSArray{
for data in datas{
if let title = data["title"] as? String {
if let street = data["street"] as? String {
if let city = data["city"] as? String {
if let stateAbv = data["stateAbv"] as? String {
if let zip = data["zip"] as? String {
self.geoAddress("\(title)", street: "\(street)", city: "\(city)", state: "\(stateAbv)", zip: "\(zip)")
}
}
}
}
}
}
}
}
} catch{}
}
}
}
task.resume()
}
func geoAddress(title: String, street: String, city: String, state: String, zip: String){
let storeName = "\(title)"
let location = "\(street) \(city) \(state) \(zip)"
let geocoder = CLGeocoder();
geocoder.geocodeAddressString(location, completionHandler: {(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if (error != nil) {
print("Error \(error!)")
} else if let placemark = placemarks?[0] {
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
let pointAnnotation:MKPointAnnotation = MKPointAnnotation()
pointAnnotation.coordinate = coordinates
pointAnnotation.title = storeName
pointAnnotation.subtitle = location
self.mapView.addAnnotation(pointAnnotation)
}
})
}
private func mapViewRegionDidChangeFromUserInteraction() -> Bool {
let view: UIView = self.mapView.subviews[0] as UIView
// Look through gesture recognizers to determine whether this region change is from user interaction
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
if( recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Ended ) {
return true
}
}
}
return false
}
func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
mapChangedFromUserInteraction = mapViewRegionDidChangeFromUserInteraction()
if (mapChangedFromUserInteraction) {
// user changed map region
}
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if (mapChangedFromUserInteraction) {
// user changed map region
let center = mapView.centerCoordinate
let mapLatitude = center.latitude
let mapLongitude = center.longitude
let locationmove = CLLocation(latitude: mapLatitude, longitude: mapLongitude)
CLGeocoder().reverseGeocodeLocation(locationmove) { (placemarks, error) in
if (error != nil){
print(error)
}else {
if let p = placemarks?[0]{
let locality = p.locality ?? ""
let administrativeArea = p.administrativeArea ?? ""
self.mappedCity = String(locality)
self.mappedState = String(administrativeArea)
self.parseJSON("\(locality)", state: "\(administrativeArea)")
}
}
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
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?.animatesDrop = false
pinView?.canShowCallout = true
pinView?.draggable = true
pinView?.pinTintColor = UIColor.greenColor()
let rightButton: AnyObject! = UIButton(type: UIButtonType.DetailDisclosure)
pinView?.rightCalloutAccessoryView = rightButton as? UIView
}
else {
pinView?.annotation = annotation
}
return pinView
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
selectedAnnotation = view.annotation as? MKPointAnnotation
performSegueWithIdentifier("Details", sender: self)
}
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
if newState == MKAnnotationViewDragState.Ending {
let droppedAt = view.annotation?.coordinate
print(droppedAt)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "Details"){
let myDetails = segue.destinationViewController as! DetailViewController
myDetails.mytitle = selectedAnnotation.title
myDetails.mysubtitle = selectedAnnotation.subtitle
}
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
}
Subclass the "MKPointAnnotation" class and add your custom property in it.
class MyAnnotation : MKPointAnnotation {
var customProperty : String?
}
And you can use MyAnnotation instead of MKPointAnnotation. Like following
let pointAnnotation:MyAnnotation = MyAnnotation()
pointAnnotation.coordinate = coordinates
pointAnnotation.title = storeName
pointAnnotation.subtitle = location
pointAnnotation.customProperty = "your value"
self.mapView.addAnnotation(pointAnnotation)
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've just got stuck trying to add a detail button to my annotation point, unfortunately I don't know how to do it. Does anyone could help me with that?
The image below presents what I'd like to achieve. Thanks!
MapKitViewController:
import UIKit
import MapKit
import CoreLocation
class MapKitViewController: UIViewController, MKMapViewDelegate
{
let locationManager = CLLocationManager()
#IBOutlet weak var nmapView: MKMapView!
override func viewDidLoad()
{
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
let location = CLLocationCoordinate2D(
latitude: 53.4265107,
longitude: 14.5520357)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
nmapView.setRegion(region, animated: true)
nmapView.showsPointsOfInterest = false
nmapView.showsUserLocation = true
displayMarkers()
}
func displayMarkers() -> Void
{
let jsonURL: NSURL = NSURL(string: "http://jsonstring.com/")!
var dataFromNetwork: NSData = NSData(contentsOfURL: jsonURL)!
let json = JSON(data: dataFromNetwork)
var jsonSize = json.count
var todaysDate:NSDate = NSDate()
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
var formattedDate:String = dateFormatter.stringFromDate(todaysDate)
let annotationView = MKAnnotationView()
let detailButton: UIButton = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as UIButton
annotationView.rightCalloutAccessoryView = detailButton
for(var i = 0; i < jsonSize; i++)
{
if(json[i]["rozpoczecie"].stringValue == formattedDate)
{
let clubID = json[i]["id_klub"].stringValue
let annotation = MKPointAnnotation()
let (resultSet, err) = SD.executeQuery("SELECT * FROM Clubs WHERE ID = ?", withArgs: [clubID])
if(err != nil){println("blad")}
else
{
for row in resultSet
{
let name = row["Name"]?.asString()
let latitude = row["Latitude"]?.asDouble()
let longitude = row["Longitude"]?.asDouble()
annotation.title = name
var markerLatitude: Double = latitude!
var markerLongitude: Double = longitude!
let location = CLLocationCoordinate2D(latitude: markerLatitude, longitude: markerLongitude)
annotation.setCoordinate(location)
annotation.subtitle = json[i]["nazwa"].stringValue
}
nmapView.addAnnotation(annotation)
}
}
}
}
You are doing it right.You just need to have these methods implemented for adding button along with title and subtitle
iOS 8 and Xcode 6
import UIKit
import MapKit
import CoreLocation
class MapKitViewController: UIViewController, MKMapViewDelegate
{
let locationManager = CLLocationManager()
#IBOutlet weak var nmapView: MKMapView!
override func viewDidLoad()
{
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
let location = CLLocationCoordinate2D(
latitude: 53.4265107,
longitude: 14.5520357)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
nmapView.setRegion(region, animated: true)
nmapView.showsPointsOfInterest = false
nmapView.showsUserLocation = true
displayMarkers()
}
// When user taps on the disclosure button you can perform a segue to navigate to another view controller
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if control == view.rightCalloutAccessoryView{
println(view.annotation.title) // annotation's title
println(view.annotation.subtitle) // annotation's subttitle
//Perform a segue here to navigate to another viewcontroller
// On tapping the disclosure button you will get here
}
}
// Here we add disclosure button inside annotation window
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
println("viewForannotation")
if annotation is MKUserLocation {
//return nil
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
//println("Pinview was nil")
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
}
var button = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as UIButton // button with info sign in it
pinView?.rightCalloutAccessoryView = button
return pinView
}
func displayMarkers() -> Void
{
let jsonURL: NSURL = NSURL(string: "http://atnight.wtznc.com/json.php")!
var dataFromNetwork: NSData = NSData(contentsOfURL: jsonURL)!
let json = JSON(data: dataFromNetwork)
var jsonSize = json.count
var todaysDate:NSDate = NSDate()
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
var formattedDate:String = dateFormatter.stringFromDate(todaysDate)
let annotationView = MKAnnotationView()
// Adding button here wont do anything so remove these two lines
let detailButton: UIButton = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as UIButton
annotationView.rightCalloutAccessoryView = detailButton
// For adding button we have to use a method named as viewForAnnotation
for(var i = 0; i < jsonSize; i++)
{
if(json[i]["rozpoczecie"].stringValue == formattedDate)
{
let clubID = json[i]["id_klub"].stringValue
let annotation = MKPointAnnotation()
let (resultSet, err) = SD.executeQuery("SELECT * FROM Clubs WHERE ID = ?", withArgs: [clubID])
if(err != nil){println("blad")}
else
{
for row in resultSet
{
let name = row["Name"]?.asString()
let latitude = row["Latitude"]?.asDouble()
let longitude = row["Longitude"]?.asDouble()
annotation.title = name
var markerLatitude: Double = latitude!
var markerLongitude: Double = longitude!
let location = CLLocationCoordinate2D(latitude: markerLatitude, longitude: markerLongitude)
annotation.setCoordinate(location)
annotation.subtitle = json[i]["nazwa"].stringValue
}
nmapView.addAnnotation(annotation)
}
}
}
}
}
Check out my output.