I want to show user's profile pictures on map. I am storing datas on Firebase as longitude, latitude , photo as url on Firebase. Now, I am getting datas for all users on console. But I could not show these datas as annotation. Profile pictures will show on map according to longitude and latitude. How can I do this. I searched lots of websites and lots of tutorials. I could not find anything. All of shows that adding one picture on map as annotation. I want to do this for all users.`
class HomeTest: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var ref = Database.database().reference()
var storageRef = Storage.storage().reference()
let manager = CLLocationManager()
var users = [User] ()
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
fetchUser()
}
func fetchUser() {
Database.database().reference().child("locations").observe(.childAdded, with: {(snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
user.latitude = dictionary["latitude"] as! Float
user.longitude = dictionary["longitude"] as! Float
user.photo = dictionary["photo"] as! String
//user.username = dictionary["username"] as! String
print(user.latitude, user.longitude, user.photo)
self.users.append(user)
DispatchQueue.main.async(execute: {
// ???
})
}
} , withCancel: nil)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations [0]
let myLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.020,0.020)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation,span)
self.mapView.setRegion(region, animated: true)
self.mapView.isZoomEnabled = true
self.mapView.isScrollEnabled = true
self.mapView.showsUserLocation = true
}
}
Related
I am using map in Two viewcontrollers
Initially in first viewcontroller i am getting current location in map..
in second viewcontroller i am getting new location coordinates, which i am sending to firstview controller using delegate.. but here how to replace the delegate method coordinates with current location coordinates in first view controller
first view controller code: here in delegate method i am successfully having new location coordinates which i need replace with current location
in userDidEnterInformationdelegate method i am getting all values from 2nd view controller
import UIKit
import CoreLocation
import MapKit
class ProfileAddressViewController: UIViewController, CLLocationManagerDelegate, UISearchBarDelegate, DataEnteredDelegate {
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D()
let locationManager = CLLocationManager()
var latitude: Double?
var logitude: Double?
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
}
func userDidEnterInformation(info: DataEnteredModelSave) {
print("map address viewcontroller data \(info)")
self.pincodeField.text = info.pinCode
self.cityField.text = info.cityField
self.latitude = info.zLatitude
self.self.logitude = info.zLongitude
print("new map address viewcontroller data info lat long \(self.latitude) \(self.logitude)")
}
#IBAction func submitButtonClicked(_ sender: UIButton) {
self.view.endEditing(true)
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "NewZoomAddressViewController") as! NewZoomAddressViewController;
self.navigationController?.pushViewController(viewController, animated: true);
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let _: CLLocationCoordinate2D = manager.location?.coordinate else { return }
let userLocation :CLLocation = locations.last! as CLLocation
latitude = userLocation.coordinate.latitude
logitude = userLocation.coordinate.longitude
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(userLocation) { (placemarks, error) in
if (error != nil){
print("error in reverseGeocode")
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count>0{
let placemark = placemarks![0]
let placemarkDictonary: NSDictionary=placemark.addressDictionary as! NSDictionary
self.pincodeField.text=placemarkDictonary["ZIP"] as? String
self.cityField.text=placemarkDictonary["City"] as? String
}
}
let center = CLLocationCoordinate2D(latitude: latitude!, longitude: logitude!)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.setRegion(region, animated: true)
let myAnnotation: MKPointAnnotation = MKPointAnnotation()
myAnnotation.coordinate = CLLocationCoordinate2DMake(userLocation.coordinate.latitude, userLocation.coordinate.longitude);
myAnnotation.title = "Current location"
mapView.addAnnotation(myAnnotation)
}
}
please help me to add delegate method latitude and longitude in locationManager didUpdateLocations
Replace userDidEnterInformation with below code:
func userDidEnterInformation(info: DataEnteredModelSave) {
print("map address viewcontroller data \(info)")
self.pincodeField.text = info.pinCode
self.streetField.text = info.streetField
self.cityField.text = info.cityField
self.latitude = info.zLatitude
self.logitude = info.zLongitude
print("map address viewcontroller data info lat long \(self.latitude) \(self.logitude)")
locationManager.stopUpdatingLocation() //stop updating location when you got data from delegate
let userLocation = CLLocation.init(latitude: latitude!, longitude: logitude!)
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(userLocation) { (placemarks, error) in
if (error != nil){
print("error in reverseGeocode")
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count>0{
let placemark = placemarks![0]
print(placemark.locality!)
print(placemark.administrativeArea!)
print(placemark.country!)
let placemarkDictonary: NSDictionary=placemark.addressDictionary as! NSDictionary
self.pincodeField.text=placemarkDictonary["ZIP"] as? String
self.cityField.text=placemarkDictonary["City"] as? String
self.plotField.text=placemarkDictonary["Name"] as? String
self.streetField.text=placemarkDictonary["Street"] as? String
self.appormentNoField.text=placemarkDictonary["SubThoroughfare"] as? String
self.colonyField.text=placemarkDictonary["SubLocality"] as? String
self.landmarkField.text=placemarkDictonary["SubThoroughfare"] as? String
}
}
let center = CLLocationCoordinate2D(latitude: latitude!, longitude: logitude!)
//Assign data to map again with new location
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.setRegion(region, animated: true)
let myAnnotation: MKPointAnnotation = MKPointAnnotation()
myAnnotation.coordinate = CLLocationCoordinate2DMake(latitude!, logitude!)
myAnnotation.title = "Current location"
mapView.addAnnotation(myAnnotation)
}
I have added comments please check them.
EDIT: As per your second request if you want to show new coordinates on NewZoomAddressViewController first you need to pass coordinates to NewZoomAddressViewController in submitButtonClicked method like:
viewController.latestLocation = CLLocation.init(latitude: self.latitude!, longitude: self.logitude!)
then in NewZoomAddressViewController declare new var
var latestLocation: CLLocation?
and remove other code which is related to user's current location and final code will look like:
import UIKit
import MapKit
import CoreLocation
//import SwiftKeychainWrapper
protocol DataEnteredDelegate: class {
func userDidEnterInformation(info: DataEnteredModelSave)
}
class NewZoomAddressViewController: UIViewController {
#IBOutlet weak var oneBtnContainerView: UIView!
var latitudeZoom: Double?
var logitudeZoom: Double?
weak var delegate: DataEnteredDelegate? = nil
var zipName: String?
var localityName: String?
var sublocalityName: String?
var streetNumber: String?
var streetName: String?
let searchCont = UISearchController(searchResultsController: nil)
let annotation = MKPointAnnotation()
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var addressLabel: UILabel!
let regionInMeters: Double = 10000
var previousLocation: CLLocation?
var latestLocation: CLLocation?
override func viewDidLoad() {
super.viewDidLoad()
print("in Zoom map VC")
mapView.delegate = self
addressLabel.text = "\(self.sublocalityName!) \(localityName!) \(self.zipName!)"
centerViewOnUserLocation()
}
#IBAction func backBtn(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden=true
}
var viewController: UIViewController?
#IBAction func confirmBtn(_ sender: Any) {
guard
let zipName = zipName,
let sublocalityName = sublocalityName,
let localityName = localityName,
let lnatZ = latitudeZoom,
let longZ = logitudeZoom
else { return }
let enteredData = DataEnteredModelSave(pinCode: zipName, streetField: sublocalityName, cityField: localityName, zLatitude: lnatZ, zLongitude: longZ)
delegate?.userDidEnterInformation(info: enteredData)
self.navigationController?.popViewController(animated: true)
}
func centerViewOnUserLocation() {
if let location = latestLocation {
let region = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
}
func getCenterLocation(for mapView: MKMapView) -> CLLocation {
latitudeZoom = mapView.centerCoordinate.latitude
logitudeZoom = mapView.centerCoordinate.longitude
print("coordinates from zoom in func \(latitudeZoom), \(logitudeZoom)")
return CLLocation(latitude: latitudeZoom!, longitude: logitudeZoom!)
//print(CLLocation.self)
}
}
extension NewZoomAddressViewController: CLLocationManagerDelegate, UISearchBarDelegate {
// MARK:- Search Address
#IBAction func searchLocationButton(_ sender: Any) {
// let searchCont = UISearchController(searchResultsController: nil)
searchCont.searchBar.delegate = self
searchCont.searchBar.backgroundColor = .blue
present(searchCont, animated:true, completion:nil)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
dismiss(animated: true, completion: nil)
//create the search request
let searchReq = MKLocalSearch.Request()
searchReq.naturalLanguageQuery = searchBar.text
let activeSearch = MKLocalSearch(request: searchReq)
activeSearch.start { (response, error) in
UIApplication.shared.endIgnoringInteractionEvents()
if response == nil{
print("error")
}
else{
//remove annotation
//let annotations = self.mapView.annotations
// self.mapView.removeAnnotation(annotations as! MKAnnotation)
//getting data
let lat = response?.boundingRegion.center.latitude
let long = response?.boundingRegion.center.longitude
//create annotation
//let annotation = MKPointAnnotation()
self.annotation.title = searchBar.text
self.annotation.coordinate = CLLocationCoordinate2DMake(lat!, long!)
self.mapView.addAnnotation(self.annotation)
//zooming annotation
let coordinate: CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat!, long!)
let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
let region = MKCoordinateRegion(center: coordinate, span: span)
self.mapView.setRegion(region, animated: true)
// Add below code to get search address
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: lat!, longitude: long!)
geoCoder.reverseGeocodeLocation(location, completionHandler:
{
placemarks, error -> Void in
// Place details
guard let placeMark = placemarks?.first else { return }
// Location name
self.zipName = placeMark.postalCode
self.localityName = placeMark.locality
self.sublocalityName = placeMark.subLocality
self.streetNumber = placeMark.subThoroughfare
self.streetName = placeMark.thoroughfare
})
}
}
}
}
extension NewZoomAddressViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let center = getCenterLocation(for: mapView)
let geoCoder = CLGeocoder()
guard let previousLocation = self.latestLocation else { return }
guard center.distance(from: previousLocation) > 50 else { return }
self.previousLocation = center
let userLocation :CLLocation = center as CLLocation
latitudeZoom = userLocation.coordinate.latitude
logitudeZoom = userLocation.coordinate.longitude
print("snajxhdwuidhwiuqhdxiqwjmdio \(latitudeZoom), \(logitudeZoom)")
geoCoder.reverseGeocodeLocation(center) { [weak self] (placemarks, error) in
guard let self = self else { return }
if let _ = error {
//TODO: Show alert informing the user
return
}
guard let placemark = placemarks?.first else {
//TODO: Show alert informing the user
return
}
self.streetNumber = placemark.subThoroughfare ?? ""
self.streetName = placemark.thoroughfare ?? ""
print("street number of zoom map \(self.streetName)")
self.localityName = placemark.locality ?? ""//locality
self.sublocalityName = placemark.subLocality ?? ""//locality
self.zipName = placemark.postalCode ?? ""//locality
DispatchQueue.main.async {
self.addressLabel.text = "\(self.streetNumber ?? "") \(self.streetName ?? "") \(self.sublocalityName ?? "") \(self.zipName ?? "") \(self.localityName ?? "")"
print("zzooom map location label \(self.addressLabel.text)")
}
}
}
}
I've just updated to the Xcode 9 and Swift 4 and it has broken at lot of my functions. One of which being how I get the user's location, zoom in on it on launch, detecting annotation selection and various other tasks. They don't work at all. My view controller class code is below.
import UIKit
import MapKit
import Firebase
import FirebaseDatabase
import Pulley
import CoreLocation
class ChildMapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
let manager = CLLocationManager()
var isInitialized = false
func startBackgroundLocationUpdates() {
self.manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
manager.pausesLocationUpdatesAutomatically = true
manager.activityType = .fitness
manager.allowsBackgroundLocationUpdates = true
manager.startMonitoringSignificantLocationChanges()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
if !isInitialized {
isInitialized = true
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
}
}
struct loc {
let title: String
let latitude: Double
let longitude: Double
}
var locs = [
loc(title: "New York, NY", latitude: 40.713054, longitude: -74.007228),
]
func placeAnnotations() {
let annotations = locs.map { location -> MKAnnotation in
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
annotation.title = location.title
return annotation
}
mapView.addAnnotations(annotations)
}
var position:PulleyPosition!
func mapView(_ didSelectmapView: MKMapView, didSelect view: MKAnnotationView) {
Shared.shared.annotation = view.annotation!
if Shared.shared.annotation is MKUserLocation {
Shared.shared.nameString = "My Location"
} else {
loadData()
}
}
func mapView(_ mapView: MKMapView, didDeselect view:
MKAnnotationView) {
let drawerContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DrawerViewController")
if let drawer = self.parent?.parent as? PulleyViewController {
drawer.setDrawerContentViewController(controller: drawerContent, animated: false)
drawer.setDrawerPosition(position: PulleyPosition.partiallyRevealed, animated: true)
}
}
func loadData() {
let number = (Shared.shared.annotation?.title)!
if let number = number {
let ref = FIRDatabase.database().reference()
ref.child("safehouses").child("\(number)").observeSingleEvent(of: .value, with: { snapshot in
let snapDict = snapshot.value as? NSDictionary
let name = snapDict?["name"] as? String
let phone = snapDict?["phone"] as? String
let current = CLLocation(latitude: (self.manager.location?.coordinate.latitude)!, longitude: (self.manager.location?.coordinate.longitude)!)
let destination = CLLocation(latitude: Shared.shared.annotation.coordinate.latitude, longitude: Shared.shared.annotation.coordinate.longitude)
let distance2 = current.distance(from: destination)
let distance1 = round(distance2)
if distance1 >= 1000 {
if distance1 < 10000 {
let distance3 = distance1/1000
let distance = Double(round(10*distance3)/10)
Shared.shared.typeString = "Safehouse · " + String(distance) + " km"
} else {
let distance3 = distance1/1000
let distance = round(distance3)
Shared.shared.typeString = "Safehouse · " + String(distance) + " km"
}
} else {
let distance = self.forTailingZero(temp: distance1)
Shared.shared.typeString = "Safehouse · " + distance + " m"
}
if let name = name {
if let phone = phone {
Shared.shared.nameString = name
Shared.shared.phoneString = phone
let drawerContent = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NewDrawerViewController")
if let drawer = self.parent?.parent as? PulleyViewController {
drawer.setDrawerContentViewController(controller: drawerContent, animated: false)
drawer.setDrawerPosition(position: PulleyPosition.partiallyRevealed, animated: true)
}
}
}
})
}
}
func forTailingZero(temp: Double) -> String{
var tempVar = String(format: "%g", temp)
return tempVar
}
func loop() {
let ref = FIRDatabase.database().reference()
ref.child("safehouses").observeSingleEvent(of: .value, with: { snapshot in
let snapDict = snapshot.value as? NSDictionary
let ii = snapDict?["number"] as? Int
let iii = Int(ii!)
var i = 1
while i <= iii{
let name = String(i)
ref.child("safehouses").child("\(i)").observeSingleEvent(of: .value, with: { snapshot in
let snapDict = snapshot.value as? NSDictionary
let longitude = snapDict?["longitude"]
as? String
let latitude = snapDict?["latitude"] as? String
if let longitude = longitude {
if let latitude = latitude {
let latitude1 = Double(latitude)
if let latitude1 = latitude1 {
let longitude1 = Double(longitude)
if let longitude1 = longitude1 {
self.locs.append(loc(title: name, latitude: latitude1, longitude: longitude1))
self.placeAnnotations()
}
}
}
}
})
i = i + 1
}
})
}
#IBOutlet weak var menuButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
self.revealViewController().rearViewRevealWidth = 175
self.revealViewController().toggleAnimationType = SWRevealToggleAnimationType.easeOut
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
loop()
mapView.delegate = self
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestAlwaysAuthorization()
Shared.shared.mapView = self.mapView
startBackgroundLocationUpdates()
}
}
final class Shared {
static let shared = Shared()
var nameString : String!
var phoneString : String!
var annotation : MKAnnotation!
var mapView : MKMapView!
var typeString : String!
}
First add these property in info.plist
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>description1</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>description2</string>
<key>NSLocationUsageDescription</key>
<string>description3</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>accept to get location</string>
<key>UIBackgroundModes</key>
And then after copy this view controller in your project
import UIKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate {
var locationManager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
determineMyCurrentLocation()
// Do any additional setup after loading the view.
}
func determineMyCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager!.allowsBackgroundLocationUpdates = true
locationManager!.pausesLocationUpdatesAutomatically = false
let value = locationManager.startMonitoringSignificantLocationChanges()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
print("ankur :- \(region)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
print("Error \(error)")
}
}
Last step :- Open Capabilities -> In background modes -> click on location updates
If any query comment it
You need to add these keys in info plist :
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
I've been searching for two days straight and Im stuck on this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
This happens when I try and annotate an array of latitude/Longitude data that am pulling from Firebase.
I am able to use this same data to display in a UITable view successfully, but the problem is when I try and annotate that data to a map.
Goal:Multiple annotations at once. to have each user that is store in Firebase, be annotated on the map with whatever lat/long firebase has for them.
I have read that perhaps I haven't initialized the map view. But I am able to add a single annotation successfully.
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var mapView: MKMapView!
var userPinView: MKAnnotationView?
var locationManager: CLLocationManager = CLLocationManager()
var startLocation: CLLocationManager!
var latitude: String?
var longitude: String?
var loc: String?
let cellId = "cellId"
let pinId = "pinId"
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handlelogout))
navigationItem.leftBarButtonItem?.tintColor = UIColor.purple
//navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Cast", style: .plain, target: self, action: #selector( handleStoreUserLocation))
var rightCastBarButtonItem: UIBarButtonItem = UIBarButtonItem(title: "Cast", style: .plain, target: self, action: #selector( handleStoreUserLocation))
var rightWhoIsCastingListBarButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.search, target: self, action: #selector(ViewController.castListTapped))
self.navigationItem.setRightBarButtonItems([rightCastBarButtonItem, rightWhoIsCastingListBarButton], animated: true)
navigationItem.rightBarButtonItem?.tintColor = UIColor.purple
checkIfUserIsLoggedIn()
fetchAllBroadcasts()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
startLocation = nil
}
//this function will grab the current users location and display it as long and lat numbers. Firebase/GEOFire will then need to reference these coordinates when broadcasting location to other users.
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation])
{
let latestLocation: CLLocation = locations[locations.count - 1]
latitude = String(format: "%.4f",
latestLocation.coordinate.latitude)
longitude = String(format: "%.4f",
latestLocation.coordinate.longitude)
// print(latitude, longitude)
}
//this grabs the logged in user Name and displays it in the center nav bar on main screen.
func checkIfUserIsLoggedIn() {
if FIRAuth.auth()?.currentUser?.uid == nil {
perform(#selector(handlelogout), with: nil, afterDelay: 0)
} else {
let uid = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("users").child(uid!).observe(.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.navigationItem.title = dictionary ["name"] as? String
}
}, withCancel: nil)
}
//adtional set up after load for MAP
var mapView = MKMapView()
//set map and grab user location
mapView.showsUserLocation = true
// mapView.showAnnotations([MKAnnotation], animated: true)
mapView.mapType = .standard
mapView.frame = view.frame
mapView.tintColor = UIColor.purple
mapView.delegate = self
view.addSubview(mapView)
//******Defalut Map Location********
var location = CLLocationCoordinate2D(
latitude: 49.2810,
longitude: -123.0733
)
//tell the map what the area spanned by the region is
var span = MKCoordinateSpanMake(0.2, 0.2)
//define the region
var region = MKCoordinateRegion(center: location, span: span)
//set region
mapView.setRegion(region, animated: true)
// annotations for the map. this is based of the dedault location above.
// var annotation = MKPointAnnotation()
//annotation.coordinate = location
//annotation.title = "Gayge HQ"
//annotation.subtitle = "oh, hello!"
//mapView.addAnnotation(annotation)
}
//temporary "logout" function . this will later be hidden in slide out menu
func handlelogout (){
do{
try FIRAuth.auth()?.signOut()
} catch let logOutError {
print(logOutError)
}
let loginController = LoginController()
//call the constant loginController that will call the loginController.swift file
present(loginController, animated: true, completion: nil)
}
func handleRightSlideMenu (){
}
func setUpNavBarWithUser (){
}
//*****NEEDS WORK TO FECTH ALL USER LOCATIONS FROM DICTIONARY AND THEN ANNOTATE EACH USER ON THE MAP******
func fetchAllBroadcasts() {
FIRDatabase.database().reference().child("users").observe(.childAdded, with: {(snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject?] {
let user = User()
user.setValuesForKeys(dictionary)
self.users.append(user)
//this will crash because of background thread so lets use dispatch_async to fix
DispatchQueue.main.async(){
self.mapView
}
var locationArray: [CLLocationCoordinate2D] = []
var longDouble = CLLocationDegrees(user.long!)
var latDouble = CLLocationDegrees(user.lat!)
var userBroadcastLocations = CLLocationCoordinate2D(
latitude: (latDouble)!, longitude: (longDouble)!) //array of user long and lat
locationArray.append(userBroadcastLocations)
self.userPinView?.annotation
locationArray.append(userBroadcastLocations)
var annotation = MKPointAnnotation()
annotation.coordinate = userBroadcastLocations
annotation.title = "title"
annotation.subtitle = "testing"
//self.mapView.addAnnotations([locationArray.MKPinAnnotation])
self.mapView.showAnnotations([locationArray as! MKAnnotation], animated: true)
}
}, withCancel: nil)
}
//launch a list view of all users broadcasting via seperate viewController (BroadcastListController)
func castListTapped(send: UIButton){
let broadcastListController = BroadcastListController()
let navController = UINavigationController(rootViewController: broadcastListController)
present(navController, animated: true, completion: nil)
}
func handleStoreUserLocation(){
let uid = FIRAuth.auth()?.currentUser?.uid
let ref = FIRDatabase.database().reference().child("users").child(uid!)
let childRef = ref.childByAutoId()
if latitude == nil && longitude == nil {
let values = ["lat": 0, "long": 0]
ref.updateChildValues(values)
}else{
let values = ["lat": latitude, "long": longitude]
ref.updateChildValues(values)
}
}
}
I'll be honest, I've probably read the answer, problem is the answers are so advanced that I can't tell what is applicable to my code or not. Hence this is why I am reaching out for support. Thank you for all you can do.
Hello I am developing an application for ios and one of it's functions has to find the user's current location. Here is my code:
import UIKit
import MapKit
import CoreLocation
import SwiftyJSON
struct City {
let name : String
let location : CLLocation
let description :String
let imageName : String
func distanceTo(location:CLLocation) -> Int
{
let distanceMeters = location.distanceFromLocation(self.location)
let distanceKilometers = distanceMeters / 1000.00
return Int(round(100 * distanceKilometers) / 100)
}
}
class FirstViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var LabelTest: UILabel!
#IBOutlet weak var Slider: UISlider!
#IBOutlet weak var LabelValueSlider: UILabel!
var MySliderCurrentValue = Double()
var manager = CLLocationManager()
var userLoc: CLLocationCoordinate2D!
{
willSet{
self.orderCitysByProximity()
self.filterCitysByProximity(Int(self.Slider.value))
self.LocateMe(manager)
}
}
var cities = [City]()
var nearbyCities = [City]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let path: String = NSBundle.mainBundle().pathForResource("cities", ofType: "json") as String!
let jsonData = NSData(contentsOfFile: path) as NSData!
//let ReadableJSON = JSON ( data:jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil )
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
for city in jsonObject["cities"] as! [[String:AnyObject]] {
//let coordinates = position["Position"] as! [String:CLLocationDegrees]
let cityName = city["Name"] as! String
let latitude = city["Latitude"] as! Double
let longitude = city["Longitude"] as! Double
let description = city["Description"] as! String
let image = city["Image"] as! String
let location = CLLocation(latitude: latitude, longitude: longitude)
let city = City(name: cityName, location: location,description: description, imageName: image)
cities.append(city)
// print(cities)
}
self.orderCitysByProximity()
self.filterCitysByProximity(Int(self.Slider.value))
} catch let error as NSError {
print(error)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func SliderChange(sender: UISlider) {
let MySliderCurrentValue: String = String(Int(sender.value))
LabelValueSlider.text = MySliderCurrentValue
self.filterCitysByProximity(Int(sender.value))
}
#IBAction func goToTableView()
{
if let tableViewController = self.storyboard?.instantiateViewControllerWithIdentifier("tableViewController") as? TableViewController
{
tableViewController.cities = self.nearbyCities
self.navigationController?.pushViewController(tableViewController, animated: true)
}
}
func filterCitysByProximity(kilometers:Int)
{
self.nearbyCities.removeAll()
for city in self.cities {
if(city.distanceTo(self.userLoc) <= kilometers*2)
{
self.nearbyCities.append(city)
}
}
self.LabelTest.text = "you have \(self.nearbyCities.count) cities nearby"
let OrderedArray = self.nearbyCities.sort({ $0.distanceTo(self.userLoc) < $1.distanceTo(self.userLoc) })
self.nearbyCities = OrderedArray
}
func orderCitysByProximity()
{
let OrderedArray = self.cities.sort({ $0.distanceTo(self.userLoc) < $1.distanceTo(self.userLoc) })
self.cities = OrderedArray
}
#IBAction func LocateMe(sender: AnyObject) {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
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)
self.userLoc = location
}
}
On the bottom you will see the func locationManager, where I am trying to find the user's current location. Would you please advise me how to start this function so that the app recognise when I type userLoc. userLoc are the coordinations of the user and I have to use them in other functions like func filterCitysByProximity, and the rest, which you can see in the code.
Reason of your problem :-
When you call the delegate of the CLLocationManager in viewDidLoad() it takes some time to retrieve the usersCurrent Location , but even before usersLocation could relay back to you, your functions requiring usersCurrent Location were being triggered as you had called them in viewDidLoad() thus causing the error!
your Code should look like this:-
import UIKit
import MapKit
import CoreLocation
import SwiftyJSON
struct City {
let name : String
let location : CLLocation
let description :String
let imageName : String
func distanceTo(location:CLLocation) -> Int
{
let distanceMeters = location.distanceFromLocation(self.location)
let distanceKilometers = distanceMeters / 1000.00
return Int(round(100 * distanceKilometers) / 100)
}
}
class FirstViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var LabelTest: UILabel!
#IBOutlet weak var Slider: UISlider!
#IBOutlet weak var LabelValueSlider: UILabel!
var MySliderCurrentValue = Double()
var locationManager = CLLocationManager()
var userLoc : CLLocation!
var cities = [City]()
var nearbyCities = [City]()
override func viewDidLoad() {
super.viewDidLoad()
let path: String = NSBundle.mainBundle().pathForResource("cities", ofType: "json") as String!
let jsonData = NSData(contentsOfFile: path) as NSData!
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
for city in jsonObject["cities"] as! [[String:AnyObject]] {
//let coordinates = position["Position"] as! [String:CLLocationDegrees]
let cityName = city["Name"] as! String
let latitude = city["Latitude"] as! Double
let longitude = city["Longitude"] as! Double
let description = city["Description"] as! String
let image = city["Image"] as! String
let location = CLLocation(latitude: latitude, longitude: longitude)
let city = City(name: cityName, location: location,description: description, imageName: image)
cities.append(city)
}
} catch let error as NSError {
print(error)
}
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestWhenInUseAuthorization()
locationManager.startMonitoringSignificantLocationChanges()
locationManager.startUpdatingLocation()
if locationManager.respondsToSelector(#selector(locationManager.requestWhenInUseAuthorization)) {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
else {
locationManager.startUpdatingLocation()
}
}
#IBAction func SliderChange(sender: UISlider) {
let MySliderCurrentValue: String = String(Int(sender.value))
LabelValueSlider.text = MySliderCurrentValue
if userLoc != nil{
self.filterCitysByProximity(Int(sender.value), location: userLoc)
}else{
let alertC : UIAlertController = UIAlertController(title: "Iwin", message: "UserLocation Not Updated wait a while", preferredStyle: .Alert)
let okAction : UIAlertAction = UIAlertAction(title: "oK", style: .Default, handler: { (okActn) in
print("User pressed ok")
})
alertC.addAction(okAction)
alertC.popoverPresentationController?.sourceView = view
alertC.popoverPresentationController?.sourceRect = view.frame
self.presentViewController(alertC, animated: true, completion: nil)
}
}
//Segue to .. :-
#IBAction func goToTableView()
{
if let tableViewController = self.storyboard?.instantiateViewControllerWithIdentifier("tableViewController") as? TableViewController
{
tableViewController.cities = self.nearbyCities
print(nearbyCities)
self.navigationController?.pushViewController(tableViewController, animated: true)
}
}
#IBAction func LocateMe(sender: AnyObject) {
}
//Class Functions call :-
func filterCitysByProximity(kilometers:Int, location: CLLocation)
{
self.nearbyCities.removeAll()
for city in self.cities {
if(city.distanceTo(location) <= kilometers*2)
{
self.nearbyCities.append(city)
}
}
self.LabelTest.text = "you have \(self.nearbyCities.count) cities nearby"
let OrderedArray = self.nearbyCities.sort({ $0.distanceTo(location) < $1.distanceTo(location) })
self.nearbyCities = OrderedArray
}
func orderCitysByProximity(location: CLLocation)
{
let OrderedArray = self.cities.sort({ $0.distanceTo(location) < $1.distanceTo(location) })
self.cities = OrderedArray
}
//Location Manager Functions :-
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLoc = locations[0] as CLLocation
locationManager.stopUpdatingLocation()
print(userLoc)
self.orderCitysByProximity(userLoc)
self.filterCitysByProximity(Int(self.Slider.value), location : userLoc)
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
print("authorised call came . . . . ")
locationManager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError){
print(error.localizedDescription)
locationManager.stopUpdatingLocation()
}
}
Add privacy - location usage description : Result to your info.plist
Also if your didUpdateLocations function doesn't get called
reset your location Services by going into settings->General->Reset
Try google, you will find multiple questions
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.