In my code I am trying to access a users current location and show it on the map. I am currently getting a fatal error Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value The error is inside ViewDidLoad. What is the proper way to do so? I have all 3 of the correct privacy location *Always and when in use, when in use usage description , always usage description *
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
private var locationManager: CLLocationManager!
private var currentLocation: CLLocation?
//let locationManager = CLLocationManager()
let mapView = MKMapView()
//var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled(){
locationManager.requestAlwaysAuthorization()//This where the fatal error appears
locationManager.requestWhenInUseAuthorization()////This where the fatal error appears
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
mapView.delegate = self
mapView.mapType = .standard
mapView.isZoomEnabled = true
mapView.isScrollEnabled = true
let leftMargin:CGFloat = 10
let topMargin:CGFloat = 60
let mapWidth:CGFloat = view.frame.size.width
let mapHeight:CGFloat = view.frame.size.width
mapView.frame = CGRect(x: leftMargin, y: topMargin, width: mapWidth, height: mapHeight)
view.addSubview(mapView)
if let coor = mapView.userLocation.location?.coordinate{
mapView.setCenter(coor, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
defer { currentLocation = locations.last }
if currentLocation == nil {
// Zoom to user location
if let userLocation = locations.last {
let viewRegion = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 2000, longitudinalMeters: 2000)
mapView.setRegion(viewRegion, animated: false)
}
}
}
func checkLocationAuthorization(authorizationStatus: CLAuthorizationStatus? = nil) {
switch (authorizationStatus ?? CLLocationManager.authorizationStatus()) {
case .authorizedAlways, .authorizedWhenInUse:
locationManager.startUpdatingLocation()
mapView.showsUserLocation = true
case .restricted, .denied:
// show alert instructing how to turn on permissions
print("Location Servies: Denied / Restricted")
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
}
}
}
Assigning requestAlwaysAuthorization and requestWhenInUseAuthorization to locationManager before creating an Instance will crash the application.
Update the code like below,
if CLLocationManager.locationServicesEnabled(){
locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
Related
I am viewing a location on my first view controller. I want to also view the same location on a second view controller. How can I pass the location on the first view controller to the second view controller? or Trying to pass annotation from one view controller to another?
import UIKit
import Foundation
import MapKit
import CoreLocation
class FirstViewController: UIViewController, CLLocationManagerDelegate, UISearchBarDelegate, MKMapViewDelegate {
//Map
#IBOutlet weak var Map: MKMapView!
let manager = CLLocationManager()
var directionsArray: [MKDirections] = []
//user location constanatly updates
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
//my location coordinates????
let myLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude,location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegion(center: myLocation, span: span)
Map.showsScale = true
Map.showsPointsOfInterest = true
Map.setRegion(region, animated: true)
self.Map.showsUserLocation = true
}
// Search Button functions
#IBAction func SearchButton(_ sender: Any)
{
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.delegate = self
present(searchController, animated: true, completion: nil)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
//Ignoring user
UIApplication.shared.beginIgnoringInteractionEvents()
//Activity Indicator
let activityIndicator = UIActivityIndicatorView()
activityIndicator.style = UIActivityIndicatorView.Style.gray
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.startAnimating()
self.view.addSubview(activityIndicator)
//hide search bar
searchBar.resignFirstResponder()
dismiss(animated: true, completion: nil)
//Create the search request
let searchRequest = MKLocalSearch.Request()
searchRequest.naturalLanguageQuery = searchBar.text
let activeSearch = MKLocalSearch(request: searchRequest)
activeSearch.start{ (response, error) in
activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
if response == nil
{
print("Error")
}
else
{
//Remove annotations
let annotations = self.Map.annotations
self.Map.removeAnnotations(annotations)
//Getting Data
let latitude = response?.boundingRegion.center.latitude
let longitude = response?.boundingRegion.center.longitude
//Create annotation
let annotation = MKPointAnnotation()
annotation.title = searchBar.text
//annotation.coordinate = locationManager.location!.coordinate
annotation.coordinate = CLLocationCoordinate2DMake(latitude!, longitude!)
self.Map.addAnnotation(annotation)
//Zooming in annotation
let coordinate:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude!, longitude!)
let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
let region = MKCoordinateRegion(center: coordinate,span: span)
self.Map.setRegion(region, animated: true)
let sourceCoordinates = self.manager.location?.coordinate
let destinationCoodinates = CLLocationCoordinate2DMake(latitude!, longitude!)
let sourcePlacemark = MKPlacemark(coordinate: sourceCoordinates!)
let destPlacemark = MKPlacemark(coordinate: destinationCoodinates)
let sourceItem = MKMapItem(placemark: sourcePlacemark)
let destItem = MKMapItem(placemark: destPlacemark)
let directionsRequest = MKDirections.Request()
directionsRequest.source = sourceItem
directionsRequest.destination = destItem
directionsRequest.transportType = .automobile
let directions = MKDirections(request: directionsRequest)
directions.calculate(completionHandler: { response, error in
guard let response = response else {
if let error = error {
print("Something Went Wrong")
}
return
}
// reset/clear prevous search route
resetMapView(withNew: directions)
// display single best route on map with a polyline
let route = response.routes[0]
self.Map.addOverlay(route.polyline, level: .aboveRoads)
// animate/draw route on map
let rekt = route.polyline.boundingMapRect
self.Map.setRegion(MKCoordinateRegion(rekt),animated: true)
})
//function removes current route on map
func resetMapView(withNew directions: MKDirections){
self.Map.removeOverlays(self.Map.overlays)
self.directionsArray.append(directions)
let _ = self.directionsArray.map { $0.cancel() }
}
}
}
}
//******************************************************************************************
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
// function displays route on map
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 5.0
return renderer;
}
}
// Second View controller code below
import UIKit
import Foundation
import MapKit
import CoreLocation
class SecondViewController: UIViewController, CLLocationManagerDelegate, UISearchBarDelegate, MKMapViewDelegate {
#IBOutlet weak var Map2: MKMapView!
let manager = CLLocationManager()
var directionsArray: [MKDirections] = []
//************************************************************
//user location constanatly updates
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
// my location coordinates
let myLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude,location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegion(center: myLocation, span: span)
Map2.showsScale = true
Map2.showsPointsOfInterest = true
Map2.setRegion(region, animated: true)
self.Map2.showsUserLocation = true
}
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
// function displays route on map
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 5.0
return renderer;
}
}
There is no error but I can not get the location from my current location to what I search. Can someone please help. It says this with I click on the Car logo to get directions: MKDirectionsErrorCode=7, NSLocalizedDescription=Directions Not Available}
I am only missing the line to get to the direction. When I click on the car logo, it just does not do anything but zoom out to my current location. I am missing anything or did something wrong?
Here is my code:
protocol HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark)
}
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView! // Handle the Map Kit View
var selectedPin:MKPlacemark? = nil //Any incoming placemarks
// Destination for directions*********
var destination:MKMapItem = MKMapItem()
var MyPosition = CLLocationCoordinate2D()
var resultSearchController:UISearchController? = nil // Keep the UISearchController in memory after it's created
var locationManager = CLLocationManager() // Core Location
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest // Accuracy level
locationManager.requestWhenInUseAuthorization() // Triggers the location permission dialog
locationManager.startUpdatingLocation()
//locationManager.requestLocation() // Trigers a one-time location request
// Set up the search results table
let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable") as! LocationSearchTable
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable
// Set up the search bar
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Search for places"
navigationItem.titleView = resultSearchController?.searchBar
// Configure the UISearchController appearance
resultSearchController?.hidesNavigationBarDuringPresentation = false
resultSearchController?.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
locationSearchTable.mapView = mapView // Passes along a handle of the mapView
locationSearchTable.handleMapSearchDelegate = self // Handles the map search
}
//Getting direction of location
#objc func getDirections(sender: AnyObject){
if let selectedPin = selectedPin {
let mapItem = MKMapItem(placemark: selectedPin)
//let launchOptions = [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving]
//mapItem.openInMaps(launchOptions: launchOptions)
let request = MKDirectionsRequest()
request.source = MKMapItem.forCurrentLocation()
request.destination = destination
request.requestsAlternateRoutes = false
let directions = MKDirections(request: request)
// 8.
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
let route = response.routes[0]
self.mapView.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
}
/*
directions.calculate(completionHandler: {(response: MKDirectionsResponse!, error: Error!) in
if error != nil {
print("Error \(error)")
} else {
self.displayRout(response)
var overlays = self.mapView.overlays
for route in response.routes {
self.mapView.add(route.polyline, level: MKOverlayLevel.aboveRoads)
for next in route.steps {
print(next.instructions)
}
}
}
})
}
*/
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 4.0
return renderer
}
}
//destination = MKMapItem(placemark: selectedPin!)
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : CLLocationManagerDelegate {
// When user responds to the permission dialog
private func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
// When location information comes back (Zoom to the user's current location)
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
//****
MyPosition = location.coordinate
locationManager.stopUpdatingLocation()
let span = MKCoordinateSpanMake(0.01, 0.01)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.setRegion(region, animated: true)
}
}
// Print out the error
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error finding location: \(error.localizedDescription)")
}
}
// Searches the location and city/state
extension ViewController: HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark){
// cache the pin
selectedPin = placemark
// clear existing pins
mapView.removeAnnotations(mapView.annotations)
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let city = placemark.locality,
let state = placemark.administrativeArea {
annotation.subtitle = "\(city) \(state)"
}
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.01, 0.01)
let region = MKCoordinateRegionMake(placemark.coordinate, span)
mapView.setRegion(region, animated: true)
}
}
// Shows Pins and Car Logo
extension ViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.pinTintColor = UIColor.red
pinView?.canShowCallout = true
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPoint.zero, size: smallSquare))
button.setBackgroundImage(UIImage(named: "Car"), for: .normal)
button.addTarget(self, action: #selector(getDirections), for: .touchUpInside)
pinView?.leftCalloutAccessoryView = button
return pinView
}
}
Have a look at below code for reference hope it helps :
1 using two locations here as source and as destination and drawing a path according to locations
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
let sourceLocation = CLLocationCoordinate2D(latitude: 40.759011, longitude: -73.984472)
let destinationLocation = CLLocationCoordinate2D(latitude: 40.748441, longitude: -73.985564)
// 3.
let sourcePlacemark = MKPlacemark(coordinate: sourceLocation, addressDictionary: nil)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation, addressDictionary: nil)
// 4.
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
// 5.
let sourceAnnotation = MKPointAnnotation()
sourceAnnotation.title = "Times Square"
if let location = sourcePlacemark.location {
sourceAnnotation.coordinate = location.coordinate
}
let destinationAnnotation = MKPointAnnotation()
destinationAnnotation.title = "Empire State Building"
if let location = destinationPlacemark.location {
destinationAnnotation.coordinate = location.coordinate
}
// 6.
self.mapView.showAnnotations([sourceAnnotation,destinationAnnotation], animated: true )
// 7.
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceMapItem
directionRequest.destination = destinationMapItem
directionRequest.transportType = .walking
// Calculate the direction
let directions = MKDirections(request: directionRequest)
// 8.
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
let route = response.routes[0]
self.mapView.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 4.0
return renderer
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Output :
I am trying to add a start button to start up my navigation and to find my current location, but nothing happens after my button is pressed?
Any help would be greatly appreciated!
Note: Map does load up, but the locationManager function does nothing, its just like it hasn't been pressed.
Heres my code:
import UIKit
import MapKit
import CoreLocation
class ThirdViewController: UIViewController , CLLocationManagerDelegate{
let manager = CLLocationManager()
func START(){
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01,0.01) //shows the size of map screen
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude,location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
map.setRegion(region, animated: true)
self.map.showsUserLocation = true
}
}
#IBAction func STARTNAV(_ sender: UIButton) {
START()
}
Use didUpdateLocations delegate method for getting current coordinates
& add Maps configuration in plist file as
//MARK: locations ...
let locationManager = CLLocationManager()
func start() {
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 100.0;
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
//locationManager.requestWhenInUseAuthorization()
let authorizationStatus = CLLocationManager.authorizationStatus()
let selector = #selector(self.locationManager.requestWhenInUseAuthorization)
if self.locationManager.responds(to:selector) {
if authorizationStatus == .authorizedAlways
|| authorizationStatus == .authorizedWhenInUse {
self.locationManager.startUpdatingLocation()
}else{
self.locationManager.requestWhenInUseAuthorization()
}
}else{
self.locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print((locationManager.location?.coordinate.latitude) ?? "No values")
let status = CLAuthorizationStatus.self
print("status-------->\(status)")
let locationValue : CLLocationCoordinate2D = (manager.location?.coordinate)!
let location = CLLocation(latitude: locationValue.latitude, longitude: locationValue.longitude)
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
if error != nil{
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
if placemarks!.count > 0 {
let pm = placemarks![0]
if let locationName = pm.addressDictionary!["SubLocality"] as? NSString {
print("locationName is \(locationName)")
}
}
else{
print("Problem with the data received from geocoder")
}
})
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("locationManager-failed")
}
I have project in IOS that monitor user location and when he pass near place he want , the app will notify him .. this is my code
import UIKit
import MapKit
import CoreLocation
import UserNotifications
import FirebaseDatabase
import FirebaseAuth
class testingViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var users: User?
var todoList = [Todo] ()
let usersRef = FIRDatabase.database().reference(withPath: "Users")
var snapshot: [FIRDataSnapshot]! = []
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
FIRAuth.auth()?.addStateDidChangeListener() { auth, user in
if user != nil {
guard let user = user else { return }
self.users = User(authData: user)
let currentUserRef = self.usersRef.child((self.users?.uid)!)
currentUserRef.setValue(self.users?.email)
print(user.uid)
} else {
print("Not signed in")
}
}
// 2. setup locationManager
locationManager.delegate = self;
// self.locationManager = CLLocationManager()
// locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters;
// locationManager.desiredAccuracy = kCLLocationAccuracyBest
// locationManager.startMonitoringSignificantLocationChanges()
// locationManager.startUpdatingLocation()
// setup mapView
mapView.delegate = self
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow
// 4. setup test data
setupData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 1. status is not determined
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
}
// 2. authorization were denied
else if CLLocationManager.authorizationStatus() == .denied {
print("Location services were previously denied. Please enable location services for this app in Settings.")
}
// 3. we do have authorization
else if CLLocationManager.authorizationStatus() == .authorizedAlways {
// locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges();
}
}
func setupData() {
let usercur = (FIRAuth.auth()?.currentUser?.uid)!
let test = FIRDatabase.database().reference(fromURL: "https://ade-mc-trial.firebaseio.com/Data")
test.child(usercur).child("ToDoList").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
// 1. check if system can monitor regions
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
// 2. region data
let title = "medical collage"
let coordinate = CLLocationCoordinate2DMake(25.900767, 45.3411889)
let regionRadius = 350.0
// 3. setup region
let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: coordinate.latitude,
longitude: coordinate.longitude), radius: regionRadius, identifier: title)
self.locationManager.startMonitoring(for: region)
// 4. setup annotation
let restaurantAnnotation = MKPointAnnotation()
restaurantAnnotation.coordinate = coordinate;
restaurantAnnotation.title = "\(title)";
self.mapView.addAnnotation(restaurantAnnotation)
// 5. setup circle
let circle = MKCircle(center: coordinate, radius: regionRadius)
self.mapView.add(circle)
}
else {
print("System can't track regions")
}
}})
}
// 6. draw circle
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let circleRenderer = MKCircleRenderer(overlay: overlay)
circleRenderer.strokeColor = UIColor.blue
circleRenderer.lineWidth = 1.0
return circleRenderer
}
// 1. user enter region
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
let notification = UILocalNotification()
notification.fireDate = NSDate(timeIntervalSinceNow: 0) as Date
notification.alertBody = "Hey you! Yeah you! Swipe to unlock!"
notification.alertAction = "be awesome!"
notification.soundName = UILocalNotificationDefaultSoundName
notification.userInfo = ["CustomField1": "w00t"]
UIApplication.shared.scheduleLocalNotification(notification)
/*
guard let settings = UIApplication.shared.currentUserNotificationSettings else { return }
if settings.types == .none {
let ac = UIAlertController(title: "Can't schedule", message: "Either we don't have permission to schedule notifications, or we haven't asked yet.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(ac, animated: true, completion: nil)
return
}*/
}
/* func showAlert(_ title: String) {
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}*/
}
But when I want to link it with database - firebase- it force me to correct three lines to add " self ". I add star in lines that force me to add "self".. then the code does not work and did not display notification after add 'self'
import UIKit
import MapKit
import CoreLocation
import UserNotifications
import FirebaseDatabase
import FirebaseAuth
class testingViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var users: User?
var todoList = [Todo] ()
let usersRef = FIRDatabase.database().reference(withPath: "Users")
var snapshot: [FIRDataSnapshot]! = []
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
FIRAuth.auth()?.addStateDidChangeListener() { auth, user in
if user != nil {
guard let user = user else { return }
self.users = User(authData: user)
let currentUserRef = self.usersRef.child((self.users?.uid)!)
currentUserRef.setValue(self.users?.email)
print(user.uid)
} else {
print("Not signed in")
}
}
// 2. setup locationManager
locationManager.delegate = self;
// self.locationManager = CLLocationManager()
// locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters;
// locationManager.desiredAccuracy = kCLLocationAccuracyBest
// locationManager.startMonitoringSignificantLocationChanges()
// locationManager.startUpdatingLocation()
// setup mapView
mapView.delegate = self
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow
// 4. setup test data
setupData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 1. status is not determined
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
}
// 2. authorization were denied
else if CLLocationManager.authorizationStatus() == .denied {
print("Location services were previously denied. Please enable location services for this app in Settings.")
}
// 3. we do have authorization
else if CLLocationManager.authorizationStatus() == .authorizedAlways {
// locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges();
}
}
func setupData() {
let usercur = (FIRAuth.auth()?.currentUser?.uid)!
let test = FIRDatabase.database().reference(fromURL: "https://ade-mc-trial.firebaseio.com/Data")
test.child(usercur).child("ToDoList").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
// 1. check if system can monitor regions
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
// 2. region data
let title = "medical collage"
let coordinate = CLLocationCoordinate2DMake(25.900767, 45.3411889)
let regionRadius = 350.0
// 3. setup region
let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: coordinate.latitude,
longitude: coordinate.longitude), radius: regionRadius, identifier: title)
self.locationManager.startMonitoring(for: region)//*****************
// 4. setup annotation
let restaurantAnnotation = MKPointAnnotation()
restaurantAnnotation.coordinate = coordinate;
restaurantAnnotation.title = "\(title)";
self.mapView.addAnnotation(restaurantAnnotation)//***************
// 5. setup circle
let circle = MKCircle(center: coordinate, radius: regionRadius)
self.mapView.add(circle)//*******************************
}
else {
print("System can't track regions")
}
}})
}
// 6. draw circle
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let circleRenderer = MKCircleRenderer(overlay: overlay)
circleRenderer.strokeColor = UIColor.blue
circleRenderer.lineWidth = 1.0
return circleRenderer
}
// 1. user enter region
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
let notification = UILocalNotification()
notification.fireDate = NSDate(timeIntervalSinceNow: 0) as Date
notification.alertBody = "Hey you! Yeah you! Swipe to unlock!"
notification.alertAction = "be awesome!"
notification.soundName = UILocalNotificationDefaultSoundName
notification.userInfo = ["CustomField1": "w00t"]
UIApplication.shared.scheduleLocalNotification(notification)
}
}
Please any one have the solution?
I think the closure is not understanding the self.
In general term you should use the weak reference of self instead of the strong reference. Try by creating the weak reference of the self and putting it in the closure.
The code will be changed as below.
func setupData() {
weak var weakSelf = self //Updates
let usercur = (FIRAuth.auth()?.currentUser?.uid)!
let test = FIRDatabase.database().reference(fromURL: "https://ade-mc-trial.firebaseio.com/Data")
test.child(usercur).child("ToDoList").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
// 1. check if system can monitor regions
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
// 2. region data
let title = "medical collage"
let coordinate = CLLocationCoordinate2DMake(25.900767, 45.3411889)
let regionRadius = 350.0
// 3. setup region
let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: coordinate.latitude,
longitude: coordinate.longitude), radius: regionRadius, identifier: title)
weakSelf.locationManager.startMonitoring(for: region)//Updates
// 4. setup annotation
let restaurantAnnotation = MKPointAnnotation()
restaurantAnnotation.coordinate = coordinate;
restaurantAnnotation.title = "\(title)";
weakSelf.mapView.addAnnotation(restaurantAnnotation)//Updates
// 5. setup circle
let circle = MKCircle(center: coordinate, radius: regionRadius)
weakSelf.mapView.add(circle)//Updates
}
else {
print("System can't track regions")
}
}})
}
I'm trying to display some shops in my map and it's working fine (the second time the user go to that MapViewController, but the first time (when it's asking for the user permission location) it only displays the user location and the map is not "zoomed" in the user location.
I'm going to show my code, it's very straightforward and simple:
Updated with the new code (it's still not working and the "didChangeAuthorizationStatus" is not printing anything:
import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
let LoadURL = "http://www.website.es/shops.json"
var coordinates = CLLocation()
#IBOutlet weak var mapView:MKMapView!
var farmacia = [Farmacia]()
let locationManager = CLLocationManager()
var currentLocation = CLLocation()
var latitudeValor = String()
var longitudeValor = String()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
// Request for a user's authorization for location services
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
requestLocation()
}
}
func requestLocation () {
let status = CLLocationManager.authorizationStatus()
if status == CLAuthorizationStatus.AuthorizedWhenInUse || status == CLAuthorizationStatus.AuthorizedAlways {
self.mapView.showsUserLocation = true
var currentLocation = CLLocation()
print(locationManager.location)
if locationManager.location != nil
{
currentLocation = locationManager.location!
let center = CLLocationCoordinate2D(latitude: currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
latitudeValor = String(currentLocation.coordinate.latitude)
longitudeValor = String(currentLocation.coordinate.longitude)
self.mapView.setRegion(region, animated: true)
requestPost()
mapView.delegate = self
}
}
}
func locationManager(locationManager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .NotDetermined:
self.locationManager.requestWhenInUseAuthorization()
break
case .AuthorizedWhenInUse:
self.locationManager.startUpdatingLocation()
requestLocation()
break
case .AuthorizedAlways:
self.locationManager.startUpdatingLocation()
requestLocation()
break
case .Restricted:
// restricted by e.g. parental controls. User can't enable Location Services
break
case .Denied:
// user denied your app access to Location Services, but can grant access from Settings.app
break
}
}
/*
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
let location = locations.last as! CLLocation
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)
requestPost()
mapView.delegate = self
}
*/
func requestPost () {
let myUrl = NSURL(string: "http://www.website.es/shops_by_position.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST"
let postString = "latitude="+latitudeValor+"&longitude="+longitudeValor
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
// JSON RESULTADO ENTERO
//let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)!
//print("responseString = \(responseString)")
if error != nil
{
//print("error=\(error)")
return
}
else
{
self.farmacia = self.parseJsonData(data!)
}
}
task.resume()
}
func parseJsonData(data: NSData) -> [Farmacia] {
let farmacias = [Farmacia]()
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
// Parse JSON data
let jsonProductos = jsonResult?["farmacias"] as! [AnyObject]
//print(jsonProductos)
for jsonProducto in jsonProductos {
let farmacia = Farmacia()
farmacia.id = jsonProducto["id"] as! String
farmacia.nombre = jsonProducto["nombre"] as! String
farmacia.latitude = jsonProducto["latitude"] as! String
farmacia.longitude = jsonProducto["longitude"] as! String
let stringLat = NSString(string: farmacia.latitude)
let stringLon = NSString(string: farmacia.longitude)
let latitude: CLLocationDegrees = stringLat.doubleValue
let longitude: CLLocationDegrees = stringLon.doubleValue
coordinates = CLLocation(latitude: latitude,longitude: longitude)
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(coordinates, completionHandler: { placemarks, error in
if error != nil
{
//print(error)
return
}
else
{
if placemarks != nil && placemarks!.count > 0 {
let placemark = placemarks?[0]
// Add Annotation
let annotation = MKPointAnnotation()
annotation.title = farmacia.nombre
annotation.coordinate = placemark!.location!.coordinate
self.mapView.addAnnotation(annotation)
}
}
})
}
}
catch let parseError {
print(parseError)
}
return farmacias
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "MyPin"
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
let detailButton: UIButton = UIButton(type: UIButtonType.DetailDisclosure)
// Reuse the annotation if possible
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if annotationView == nil
{
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView!.canShowCallout = true
annotationView!.image = UIImage(named: "pin.png")
annotationView!.rightCalloutAccessoryView = detailButton
}
else
{
annotationView!.annotation = annotation
}
return annotationView
}
func mapView(mapView: MKMapView, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == annotationView.rightCalloutAccessoryView {
performSegueWithIdentifier("PinDetail2", sender: annotationView)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "PinDetail" {
let destinationController = segue.destinationViewController as! FarmaciaDetailViewController
destinationController.titulo_farmacia = (sender as! MKAnnotationView).annotation!.title!
}
if segue.identifier == "PinDetail2" {
let destinationController = segue.destinationViewController as! FarmaciaWebDetailViewController
destinationController.nombre_farmacia = (sender as! MKAnnotationView).annotation!.title!
}
}
#IBAction func cancelToMap(segue:UIStoryboardSegue) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
My question is: What I have to change in order to show the user location zoomed and my shop entries the first time the app asks for permission and the user choose "Yes"?
It's my first time using the MapKit framework and I'm a little bit lost, so much appreciated if you can show me some light in my case.
1) Change
class MapViewController: UIViewController, MKMapViewDelegate {
to
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
2) Change
func locationManager(locationManager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .NotDetermined:
self.locationManager.requestAlwaysAuthorization()
to
self.locationManager.requestWhenInUseAuthorization()
3) Add NSLocationWhenInUseUsageDescription to Info.plist
EDIT
4) Add the following code to viewDidLoad
locationManager.delegate = self
EDIT 2
5) Add import to header
import CoreLocation
The documentation for requestWhenInUseAuthorization says
When the current authorization status is kCLAuthorizationStatusNotDetermined, this method runs asynchronously and prompts the user to grant permission to the app to use location services.
So in your code, the authorisation is requested and then execution immediately continues, eventually reaching
if status == CLAuthorizationStatus.AuthorizedWhenInUse
which fails, at status is still not determined.
There is the locationManager:didChangeAuthorizationStatus: callback in CLLocationManagerDelegate that is called once the user allows or denies location access.
I would suggest that you move your logic for the .AuthorizedWhenInUse case in a function and call it either from your viewDidLoad method for cases when authorisation is already granted or from the callback if it is not granted yet.