I recently updated to xCode 7 which uses iOS 9 and when I run the following app, the location is supposed to print to the console but that's not happening.
The app will build successfully, the map displays, but the location data doesn't print. Even if the move the map, it still doesn't print.
The code should be correct, i've imported all of the necessary frameworks and set both the NSLocationWhenInUseUsageDescription & NSLocationAlwaysUsageDescription in the Info.plist file along w/ their values.
Below is the code:
// ViewController.swift
// Maps iOS9
//
// Created by Alex Ngounou on 9/28/15.
// Copyright © 2015 Alex Ngounou. All rights reserved.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var myMap: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation() // starts accessing the user's position
// 18.328278, -65.318353 Playa Flamenco
let latitude: CLLocationDegrees = 18.328278
let longitude: CLLocationDegrees = -65.318353
let latDelta: CLLocationDegrees = 0.01
let longDelta: CLLocationDegrees = 0.01
let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
let span: MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
let region: MKCoordinateRegion = MKCoordinateRegionMake(location, span)
myMap.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = "Playa de Flamenco"
annotation.subtitle = "Culebra Island"
myMap.addAnnotation(annotation)
let uilpgr = UILongPressGestureRecognizer(target: self, action: "action:")
uilpgr.minimumPressDuration = 1.0
myMap.addGestureRecognizer(uilpgr)
}
func action(gestureRecognizer: UILongPressGestureRecognizer) {
// touchpoint
let touchPoint = gestureRecognizer.locationInView(self.myMap)
// touchpoint to location
let newLocation: CLLocationCoordinate2D = myMap.convertPoint(touchPoint, toCoordinateFromView: self.myMap)
// annotation
let newAnnotation = MKPointAnnotation()
newAnnotation.coordinate = newLocation
newAnnotation.title = "New Poing"
newAnnotation.subtitle = "added via user's touch"
myMap.addAnnotation(newAnnotation)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Try this code snippet:
// Location Manager helper stuff
func initLocationManager() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.locationServicesEnabled
locationManager.requestAlwaysAuthorization()
}
// Location Manager Delegate stuff
// If failed
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
if (error) {
print(error)
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: AnyObject[]!) {
println("locations = \(locations)")
}
// authorization status
func locationManager(manager: CLLocationManager!,
didChangeAuthorizationStatus status: CLAuthorizationStatus) {
var shouldIAllow = false
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = "Restricted Access to location"
case CLAuthorizationStatus.Denied:
locationStatus = "User denied access to location"
case CLAuthorizationStatus.NotDetermined:
locationStatus = "Status not determined"
default:
locationStatus = "Allowed to location Access"
shouldIAllow = true
}
NSNotificationCenter.defaultCenter().postNotificationName("LabelHasbeenUpdated", object: nil)
if (shouldIAllow == true) {
NSLog("Location to Allowed")
// Start location services
locationManager.startUpdatingLocation()
} else {
NSLog("Denied access: \(locationStatus)")
}
}
The "Debug/Location" of my iOS simulator wasn't set correctly. Once I changed this to "City Bicycle Ride", everything's good!
Thanks for the help!
Related
The following is a code snippet from the ViewController.swift file inside my Swift 3 project. What should I change to make it work or could you give any pointers? Do you know why it is not working and instead, going to some arbritrary location instead of my location after the notification for the current location is done?
import Cocoa
import MapKit
import CoreLocation
class ViewController: NSViewController, CLLocationManagerDelegate , MKMapViewDelegate{
#IBOutlet weak var STAVO_map: MKMapView!
var manager: CLLocationManager!
var currentLocation : CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
manager = CLLocationManager()
STAVO_map.delegate = self
STAVO_map.showsUserLocation = true
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
if CLLocationManager.locationServicesEnabled() {
manager.startUpdatingLocation()
}
DispatchQueue.main.async {
self.manager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last as! CLLocation
self.manager.stopUpdatingLocation()
let region = MKCoordinateRegionMakeWithDistance(location.coordinate, 500, 500)
self.STAVO_map.setRegion(region, animated: true)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
A few things things, first I would change didUpdateLocations to
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last as! CLLocation
manager.stopUpdatingLocation()
let region = MKCoordinateRegionMakeWithDistance(location.coordinate, 1000, 1000)
self.STAVO_map.setRegion(region, animated: true)
}
In viewDidLoad…startUpdatingLocation is an async function so you’re always going to get noLocation set until didUpdateLocations is called. I would remove the 3 lines where you set noLocation, the viewRegion and setRegion. You shouldn’t need to call startUpdatingLocation twice so you can remove the second call (besides, viewDidLoad runs in main queue by default so no need for DispatchQueue)
This is my button click code
#IBAction func loc_btn_actn(_ sender: Any) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
shyMap.showsUserLocation = true
}
this is my locationManager function
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
manager.stopUpdatingLocation()
let coordinations = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude,longitude: userLocation.coordinate.longitude)
let span = MKCoordinateSpanMake(0.2,0.2)
let region = MKCoordinateRegion(center: coordinations, span: span)
shyMap.setRegion(region, animated: true)
}
when i am clicking button i am getting app delegate error cant able to track location
I am trying to make a small iPad application which features an 'About' page. I have run into an issue when I try to change the class of the about page scene which is displayed modally, as the application crashes when the user tries to access this page. I wish to change its class to "ViewController", which is the same class as my main window (map view), to create actions from buttons on the 'About' page.
Currently I am unable to ctrl+drag to the .Swift file from the About page unless I change its class. Not sure why this is happening :(
The error printed to the console is as follows:
fatal error: unexpectedly found nil while unwrapping an Optional value
0 specialised_fatalErrorMessage(StaticString, StaticString, StaticString, Uint) -> () –
Here is my storyboard with scenes:
Here is the full ViewController.Swift code:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, UISearchBarDelegate, UIPopoverPresentationControllerDelegate {
var location: CLLocation!
let locationManager = CLLocationManager()
// Map variables
var searchController:UISearchController!
var annotation:MKAnnotation!
var localSearchRequest:MKLocalSearchRequest!
var localSearch:MKLocalSearch!
var localSearchResponse:MKLocalSearchResponse!
var error:NSError!
var pointAnnotation:MKPointAnnotation!
var pinAnnotationView:MKPinAnnotationView!
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var showSearchBar: UIBarButtonItem!
#IBOutlet weak var addButton: UIBarButtonItem!
#IBOutlet weak var moreStuff: UIBarButtonItem!
#IBAction func addButton(sender: AnyObject) {
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: self.mapView.userLocation.coordinate.latitude, longitude: self.mapView.userLocation.coordinate.longitude)
self.mapView.addAnnotation(annotation)
self.locationManager.startUpdatingLocation()
}
#IBAction func showSearchBar(sender: UIBarButtonItem!) {
searchController = UISearchController(searchResultsController: nil)
searchController.hidesNavigationBarDuringPresentation = false
self.searchController.searchBar.delegate = self
presentViewController(searchController, animated: true, completion: nil)
}
#IBAction func moreStuff(sender: AnyObject) {
self.performSegueWithIdentifier("showMoreStuff", sender:self)
}
#IBAction func refresh(sender: AnyObject) {
self.locationManager.startUpdatingLocation()
}
#IBAction func segmentedControl(sender: UISegmentedControl!) {
if sender.selectedSegmentIndex == 0{
mapView.mapType = MKMapType.Standard
}
else if sender.selectedSegmentIndex == 1{
mapView.mapType = MKMapType.Satellite
}
else if sender.selectedSegmentIndex == 2{
mapView.mapType = MKMapType.Hybrid
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.mapView.showsUserLocation = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// location delegate methods
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
self.mapView.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
print("Error code: " + error.localizedDescription)
}
func searchBarSearchButtonClicked(searchBar: UISearchBar){
searchBar.resignFirstResponder()
dismissViewControllerAnimated(true, completion: nil)
if self.mapView.annotations.count != 0{
annotation = self.mapView.annotations[0]
self.mapView.removeAnnotation(annotation)
}
localSearchRequest = MKLocalSearchRequest()
localSearchRequest.naturalLanguageQuery = searchBar.text
localSearch = MKLocalSearch(request: localSearchRequest)
localSearch.startWithCompletionHandler { (localSearchResponse, error) -> Void in
if localSearchResponse == nil{
let alertController = UIAlertController(title: nil, message: "No Such Place", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
return
}
self.pointAnnotation = MKPointAnnotation()
self.pointAnnotation.title = searchBar.text
self.pointAnnotation.coordinate = CLLocationCoordinate2D(latitude: localSearchResponse!.boundingRegion.center.latitude, longitude: localSearchResponse!.boundingRegion.center.longitude)
self.pinAnnotationView = MKPinAnnotationView(annotation: self.pointAnnotation, reuseIdentifier: nil)
self.mapView.centerCoordinate = self.pointAnnotation.coordinate
}
}
}
I am new to Swift so any help is really appreciated. Thanks!
Edit:
This is the error screen presented to me once the app crashes:
I haven't used mapKit but I still try.
Looking at your error your getting a nil crash, which indicates one of your properties is nil yet you try to use it.
Having a quick read through your code it seams you haven't initialised
var localSearchResponse....
Again i don't know the MK API very well so I am not 100 sure what I'm saying is relevant.
Have you checked that all your IBOutlets are properly connected?
On which line do you get the crash when this happens? Do you get the crash before or after changing to the abouts page?
I've worked with CLLocationManager a lot before and I have several other projects where this works just fine with the EXACT SAME code... the necessary properties are in the info.Plist as well.
I've tried this both on the simulator and device, both which worked fine in my other projects. If anyone is able to help that would be great because I'm about to lose my mind. Thank you!
Required plist properties
<key>NSLocationAlwaysUsageDescription </key>
<string>Can we use your location? </string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Can we use your location?</string>
The code in the main VC
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
let clientKey = ""
let clientSecred = ""
let pushSecret = ""
let fourSquareVersion = ""
let section = ""
var latLong = ""
var currentLat : Double?
var currentLong : Double?
var locationManager : CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
if let currentLocation : CLLocation = newLocation {
currentLat = currentLocation.coordinate.latitude
currentLong = currentLocation.coordinate.longitude
print(currentLat)
print(currentLong)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
So it turns out that there was a space after NSLocationAlwaysUsageDescription in the info.plist.
I have an app that has two ViewControllers. On the first there is a count of current speed in realtime through CLLocationManager. Also there is a label that shows current speed with update by timer (NSTimer). In second ViewController there is another Label, where this current speed has to be shown too. It shows it, but don't update. I tried to set second timer (different ways: in first VC, in second VC - there is always was an error or just nothing).
Will be grateful for help, thanks!
First VC
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var currentSpeedLabel: UILabel!
var manager = CLLocationManager()
var currentSpeed: CLLocationSpeed = CLLocationSpeed()
var timer = NSTimer()
override func viewDidLoad() {
super.viewDidLoad()
mapView.mapType = MKMapType.Hybrid
trackingMe()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func HUDMapView(sender: AnyObject) {
speedCount()
}
#IBAction func findMe(sender: AnyObject) {
trackingMe()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0] as CLLocation
manager.stopUpdatingLocation()
let location = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
mapView.setRegion(region, animated: true)
}
func trackingMe() {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
mapView.showsUserLocation = true
currentSpeedUpdate()
}
func currentSpeedUpdate() {
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("speedCount"), userInfo: nil, repeats: true)
}
func speedCount() {
currentSpeed = manager.location!.speed
currentSpeedLabel.text = String(format: "%.0f km/h", currentSpeed * 3.6)
}
override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!) {
let speedController = segue.destinationViewController as! speedViewController
currentSpeed = manager.location!.speed
speedController.showSpeed = currentSpeedLabel.text
}
}
Second VC
import UIKit
class speedViewController: UIViewController {
#IBOutlet weak var secondSpeedLabel: UILabel!
var showSpeed: String!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
secondSpeedLabel.text = showSpeed
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func back(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Project Link
You could use a Singleton to hold the LocationManager. Then you can access it from all over your app. When you move to a second VC you can either change the delegate to the second VC or just get the needed data manually.
Remember that a delegate can only point to one "receiver". Changing the delegate will stop updates in the first VC. but since it is now a Singleton you can also store information in there about past locations / speeds. When dismissing the second VC get the stored data and update.
This will keep running until you call stop()
The code was simplified a bit to illustrate the idea.
VC Code:
import UIKit
import CoreLocation
class ViewController: UIViewController, TrackerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Tracker.shared.delegate = self
Tracker.shared.start()
}
func speedUpdate(speed: CLLocationSpeed) {
print(speed)
}
}
Singleton Code:
import UIKit
import MapKit
import CoreLocation
class Tracker: NSObject, CLLocationManagerDelegate {
static var shared = Tracker()
private var manager = CLLocationManager()
private var timer = NSTimer()
var region : MKCoordinateRegion?
var currentSpeed: CLLocationSpeed = CLLocationSpeed()
weak var delegate : TrackerDelegate?
private override init() {
super.init()
manager.delegate = self
manager.requestWhenInUseAuthorization()
}
internal func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
manager.stopUpdatingLocation()
let userLocation: CLLocation = locations[0] as CLLocation
let coordinates2D = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
let span = MKCoordinateSpanMake(0.05, 0.05)
region = MKCoordinateRegion(center: coordinates2D, span: span)
currentSpeed = userLocation.speed
guard let del = delegate else {
return
}
del.speedUpdate(currentSpeed)
}
func start() {
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.startUpdatingLocation()
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("loopUpdate"), userInfo: nil, repeats: true)
}
func stop() {
timer.invalidate()
}
internal func loopUpdate() {
// restart updating
manager.startUpdatingLocation()
}
}
Delegate for the Singleton:
Add more functions, or more values to the current function to get more feedback.
protocol TrackerDelegate : class {
func speedUpdate(speed:CLLocationSpeed)
}