MKUserTrackingButton doesn't appear because of Firebase? - swift

I added in code the MapView, MKUserTrackingButton button, localManager, DispatchGroup:
let mapView: MKMapView = {
let mapView = MKMapView()
mapView.translatesAutoresizingMaskIntoConstraints = false
return mapView
}()
private var userTrackingButton: MKUserTrackingButton!
private let locationManager = CLLocationManager()
let myGroup = DispatchGroup()
var array = [Car]()
In ViewDidLoad I set:
myGroup.enter()
Get data from Firebase:
observeCars()
Wait until I get all the data from Firebase:
myGroup.notify(queue: DispatchQueue.main) {
self.view.addSubview(self.mapView)
//Here code to set the mapView in the view
self.setupUserTrackingButton()
self.locationManager.delegate = self
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
Here I set my button in the view:
private func setupUserTrackingButton() {
mapView.showsUserLocation = true
userTrackingButton = MKUserTrackingButton(mapView: mapView)
userTrackingButton.layer.backgroundColor = UIColor(white: 0.5, alpha: 1).cgColor
userTrackingButton.layer.borderColor = UIColor.white.cgColor
userTrackingButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(userTrackingButton)
///Code to set in the view the button
}
Class where I set connection to Firebase to get data:
class APIService: NSObject {
class func observeCars(completion: ((_ car: Car) -> Void)?) {
let ref = Database.database().reference()
ref.child("Cars").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: Any] {
let car = Car(dictionary: dictionary)
completion?(car)
}
}, withCancel: nil)
}
}
This is the function (written in the MainViewController) where I get the data from Firebase and add it to the array:
internal func observeCars() {
/////I think this is the part that makes It doesn't appear because
if I delete this part, it works like I aspect
APIService.observeCars {
(car) in
self.array.append(car)
print(self.array.count)
if self.array.count == totalCars {
self.myGroup.leave()
}
}
//////////
}
Any hints? Thanks

I added the MKUserTrackingButton in the mapView:
mapView.addSubview(userLocationButton)
It works to me.

Related

Can I get 3d models from web servers on Swift?

I'm working on an application with Arkit. There are many 3D models and the size is big in my app. Can I get these models out of another server (outside sites)? I'm new on swift, I can't seem to find anything on loading a 3d model from a web server.
is it enough to change the model path there? Thank you
func loadModel() {
guard let virtualObjectScene = SCNScene(named: "\(modelName).\(fileExtension)", inDirectory: "Models.scnassets/\(modelName)") else {
return
}
let wrapperNode = SCNNode()
for child in virtualObjectScene.rootNode.childNodes {
let defaults = UserDefaults.standard
wrapperNode.addChildNode(child)
}
self.addChildNode(wrapperNode)
}
All code:
import UIKit
import SceneKit
import ARKit
class VirtualObject: SCNNode {
var modelName: String = ""
var fileExtension: String = ""
var thumbImage: UIImage!
var title: String = ""
var viewController: ViewController?
override init() {
super.init()
self.name = "Virtual object root node"
}
init(modelName: String, fileExtension: String, thumbImageFilename: String, title: String) {
super.init()
self.name = "Virtual object root node"
self.modelName = modelName
self.fileExtension = fileExtension
self.thumbImage = UIImage(named: thumbImageFilename)
self.title = title
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func loadModel() {
guard let virtualObjectScene = SCNScene(named: "\(modelName).\(fileExtension)", inDirectory: "Models.scnassets/\(modelName)") else {
return
}
let wrapperNode = SCNNode()
for child in virtualObjectScene.rootNode.childNodes {
let defaults = UserDefaults.standard
wrapperNode.addChildNode(child)
}
self.addChildNode(wrapperNode)
}
func unloadModel() {
self.removeFromParentNode()
for child in self.childNodes {
child.removeFromParentNode()
}
}
func translateBasedOnScreenPos(_ pos: CGPoint, instantly: Bool, infinitePlane: Bool) {
guard let controller = viewController else {
return
}
let result = controller.worldPositionFromScreenPosition(pos, objectPos: self.position, infinitePlane: infinitePlane)
controller.moveVirtualObjectToPosition(result.position, instantly, !result.hitAPlane)
}
}
extension VirtualObject {
static func isNodePartOfVirtualObject(_ node: SCNNode) -> Bool {
if node.name == "Virtual object root node" {
return true
}
if node.parent != nil {
return isNodePartOfVirtualObject(node.parent!)
}
return false
}
static let availableObjects: [VirtualObject] = [
Anatomy()
]
}
you can load an scn file from a webserver with ip addresses like this (i used a fake ip below)
let myURL = NSURL(string: “http://110.151.153.202:80/scnfiles/myfile.scn”)
let scene = try! SCNScene(url: myURL! as URL, options:nil)
Edit:
Here’s a simple Swift PlayGrounds which pulls a test cube scn file from my github repo. You just tap anywhere and the cube loads.
import ARKit
import SceneKit
import PlaygroundSupport
class ViewController: NSObject {
var sceneView: ARSCNView
init(sceneView: ARSCNView) {
self.sceneView = sceneView
super.init()
self.setupWorldTracking()
self.sceneView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(_:))))
}
private func setupWorldTracking() {
if ARWorldTrackingConfiguration.isSupported {
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
configuration.isLightEstimationEnabled = true
self.sceneView.session.run(configuration, options: [])
}
}
#objc func handleTap(_ gesture: UITapGestureRecognizer) {
let results = self.sceneView.hitTest(gesture.location(in: gesture.view), types: ARHitTestResult.ResultType.featurePoint)
guard let result: ARHitTestResult = results.first else {
return
}
// pulls cube.scn from github repo
let myURL = NSURL(string: "https://raw.githubusercontent.com/wave-electron/scnFile/master/cube.scn")
let scene = try! SCNScene(url: myURL! as URL, options: nil)
let node = scene.rootNode.childNode(withName: "SketchUp", recursively: true)
node?.scale = SCNVector3(0.01,0.01,0.01)
let position = SCNVector3Make(result.worldTransform.columns.3.x, result.worldTransform.columns.3.y, result.worldTransform.columns.3.z)
node?.position = position
self.sceneView.scene.rootNode.addChildNode(node!)
}
}
let sceneView = ARSCNView()
let viewController = ViewController(sceneView: sceneView)
sceneView.autoenablesDefaultLighting = true
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = viewController.sceneView

(Swift 3) How to add multiple pins to a MapView

I am writing a simple app that allows you to search for a location and drop a pin there. However I can't figure out how to get this pin to stay when I try and add another one. Here's my code:
class MyMapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UISearchBarDelegate{
var isMapToBeUpdated = true
var numOfTrackedLocations = 0
let locationManager = CLLocationManager.self
let annotation = MKPointAnnotation()
var annotationTitle = ""
let appDelegate = UIApplication.shared.delegate as! AppDelegate
var longArr: [Double] = []
var latArr: [Double] = []
var cityArr: [String] = [""]
var count: Int = 0
//sets variables and links this file to the app delegate//
#IBOutlet weak var myMapView: MKMapView!
#IBAction func seachButton (_sender: Any){
let searchController = (UISearchController(searchResultsController: nil))
searchController.searchBar.delegate = self
present(searchController, animated: true, completion: nil)
}
//links the search button to the search method//
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
UIApplication.shared.beginIgnoringInteractionEvents()
let activityIndicator = UIActivityIndicatorView()
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.startAnimating()
//the above adds a search bar function, and sets the loading animation//
self.view.addSubview(activityIndicator)
searchBar.resignFirstResponder()
dismiss(animated: true, completion: nil)
let searchRequest = MKLocalSearchRequest()
searchRequest.naturalLanguageQuery = searchBar.text
let activeSearch = MKLocalSearch(request: searchRequest)
activeSearch.start { (response, error) in
//starts a search session//
activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
if response == nil
{
print ("ERROR")
}
else
{
var annotation = self.myMapView.annotations
let latitude = response?.boundingRegion.center.latitude
let longitude = response?.boundingRegion.center.longitude
self.annotation.title = searchBar.text
self.annotation.coordinate = CLLocationCoordinate2DMake(latitude!, longitude!)
self.myMapView.addAnnotation(self.annotation)
let coordinate: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude!, longitude!)
let span = MKCoordinateSpanMake(0.1, 0.1)
let region = MKCoordinateRegionMake(coordinate, span)
self.myMapView.setRegion(region, animated: true)
This is only a small portion, if you want to see anymore I can post it but it doesn't relate to the annotations. If anyone can help me with this it'd be much appreciated.
Map view has function to show multiple pins.
map.showAnnotations([arrAnnotation], animated: true)
where arrAnotation is arr of MKAnnotation.

Pushing data from Firebase into class object Swift

I have been building my first firebase application and I have run into issues attempting to retrieve data and display it on the map as an annotation.
Currently my logic is as follows : Fetch data and put it into Note Class i then try to push the information into an array which is located in NoteBrain the information appears to be being placed inside the class when i debug with print statements the data shows up. The only issue is i am having trouble pushing the data to the view controller. For some reason my loadallcoordinates is not working, i even try to type print statements and it is not showing up.
My question
Is this the best way to structure my data? Is there any way I can make my current implementation work?
Thankyou
Note Class
import Foundation
import MapKit
class noteClass : NSObject, MKAnnotation{
var title : String?
var subtitle: String?
var coordinate: CLLocationCoordinate2D
var longitude : Double
var latitude : Double
init(dictionary: [String: Any]) {
self.title = dictionary["title"] as? String ?? ""
self.subtitle = dictionary["subtitle"] as? String ?? ""
self.longitude = dictionary["longitude"] as! Double
self.latitude = dictionary["latitude"] as! Double
self.coordinate = CLLocationCoordinate2DMake((dictionary["longitude"] as? Double)!, (dictionary["latitude"] as? Double)!)
}
}
noteBrain or model class
import Foundation
import UIKit
import MapKit
import CoreLocation
import Firebase
import FirebaseDatabase
class noteBrain: UIViewController, CLLocationManagerDelegate{
var noteArray = [noteClass]()
var newNote = false
var outsideToggle = true
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.0025, 0.0025)
var manager = CLLocationManager()
func getNotes() -> Array <noteClass>
{
return noteArray
}
func appendArray(newNote : noteClass){
noteArray.append(newNote)
}
func checkInside( s1 : Int ) -> noteClass{
return noteArray[0]
}
func checkToggle() -> Bool{
return outsideToggle
}
func fetchUser() {
FIRDatabase.database().reference().child("notes").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = noteClass(dictionary: dictionary)
self.appendArray(newNote: user)
}
}, withCancel: nil)
}
}
Viewcontroller
import UIKit
import MapKit
import CoreLocation
import Firebase
import FirebaseDatabase
struct PreferencesKeys{
static let savedItems = "savedItems"
}
class ViewController: UIViewController, CLLocationManagerDelegate{
let manager = CLLocationManager()
var coordinotes : [noteClass] = []
var latitude = Double()
var noteTime = noteBrain()
//Map
#IBOutlet weak var map: MKMapView!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations[0]
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, noteTime.span)
map.setRegion(region, animated: true)
self.map.showsUserLocation = true
}
override func viewDidLoad()
{
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handleLogout))
if FIRAuth.auth()?.currentUser?.uid == nil {
perform(#selector(handleLogout), with: nil, afterDelay: 0)
}
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
noteTime.fetchUser()
appendThisTest()
loadAllCoordinotes()
}
func handleLogout() {
do {
try FIRAuth.auth()?.signOut()
} catch let logoutError {
print(logoutError)
}
let loginController = LoginController()
present(loginController, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func loadAllCoordinotes() {
for x in coordinotes
{
add(coordinote: x)
}
}
func add(coordinote: noteClass) {
map.addAnnotation(coordinote)
print(coordinote.coordinate)
print("test")
print(coordinote.title! as String)
}
func appendThisTest(){
coordinotes = noteTime.getNotes()
}
}

How to update annotations when in mapview on zoom?

I'm trying to make an app, where you get buses positions from web API. When I got the data, I put annotations on mapview for all of the coordinates i got from API. Then, when annotation is tapped on, I try to hide all the annotations which aren't on the same route as the one clicked. It works fine, but when I zoom out the annotations that weren't in the original region aren't hidden. Is there a way to refresh what is displayed when user zooms on map view?
import UIKit
import MapKit
import MBProgressHUD
import Alamofire
class MapViewController: UIViewController {
//MARK: Properties and Outlets
private var timer: NSTimer?
private lazy var dateFormatter = NSDateFormatter()
private var vehicles = [String: Vehicle]()
private var points = [CLLocationCoordinate2D]()
private let locationManager = CLLocationManager()
private var currentLocation: CLLocation?
#IBOutlet weak var mapView: MKMapView!
//MARK: Actions
#IBAction func getMyLocation(sender: UIBarButtonItem) {
getLocation()
}
//MARK: Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
dateFormatter.locale = NSLocale.currentLocale()
dateFormatter.dateFormat = "HH:mm:ss"
timer = NSTimer.scheduledTimerWithTimeInterval(30, target: self, selector: "fetchVehiclesLocations", userInfo: nil, repeats: true)
fetchVehiclesLocations()
getLocation()
}
//MARK: Get Location
private func getLocation() {
let authStatus = CLLocationManager.authorizationStatus()
if authStatus == .NotDetermined {
locationManager.requestWhenInUseAuthorization()
return
}
if authStatus == .Denied || authStatus == .Restricted {
showLocationServicesDeniedAlert()
return
}
startLocationManager()
}
//MARK: Helper methods
private func showLocationServicesDeniedAlert() {
let alert = UIAlertController(title: "Location Services Disabled", message: "Please enable location services for this app in Settings.", preferredStyle: .Alert)
let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(okAction)
presentViewController(alert, animated: true, completion: nil)
}
private func startLocationManager () {
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
showLoadingHUD()
}
}
private func stopLocationManager() {
locationManager.delegate = nil
locationManager.stopUpdatingLocation()
hideLoadingHUD()
}
private func rotateBus() {
UIView.animateWithDuration(1.0, delay: 0, options: .CurveLinear, animations: {
for (id, _) in self.vehicles {
if self.vehicles[id]!.lastPosition != nil {
let annotationView = self.mapView.viewForAnnotation(self.vehicles[id]!.annotation!)
let currentLocationPoint = self.mapView.convertCoordinate(self.vehicles[id]!.lastPosition!, toPointToView: self.mapView)
let destinationPoint = self.mapView.convertCoordinate(self.vehicles[id]!.coordinates, toPointToView: self.mapView)
let yDiff = currentLocationPoint.y - destinationPoint.y
let xDiff = destinationPoint.x - currentLocationPoint.x
let arcTan = atan(yDiff / xDiff)
var angle = CGFloat(M_PI / 2) - arcTan
if xDiff < 0 {
angle = angle + CGFloat(M_PI)
}
if angle.isNaN || angle == 0.0 {
continue
}
else {
annotationView?.transform = CGAffineTransformMakeRotation(CGFloat(angle))
}
}
}
}, completion: nil)
moveBus()
}
private func moveBus() {
UIView.animateWithDuration(28.0, delay: 0, options: .CurveLinear, animations: {
for(id, _) in self.vehicles {
if self.vehicles[id]!.lastPosition != nil {
self.vehicles[id]!.annotation?.coordinate = self.vehicles[id]!.coordinates
}
}
}, completion: nil)
}
private func createVehicles() {
print(vehicles.count)
for (id, _) in vehicles {
if vehicles[id]?.annotation == nil {
let annotation = BusAnnotation()
annotation.imageName = "bus"
annotation.coordinate = vehicles[id]!.coordinates
annotation.title = id
vehicles[id]?.annotation = annotation
mapView.addAnnotation(annotation)
}
}
print(mapView.annotations.count)
rotateBus()
}
private func drawPolyline() {
let myPolyline = MKPolyline(coordinates: &points, count: points.count)
mapView.addOverlay(myPolyline)
}
//MARK: HUD display methods
private func showLoadingHUD() {
let hud = MBProgressHUD.showHUDAddedTo(mapView, animated: true)
hud.labelText = "Getting Your Location..."
}
private func hideLoadingHUD() {
MBProgressHUD.hideAllHUDsForView(mapView, animated: true)
}
//MARK: Networking
func fetchVehiclesLocations() {
let URL = urlVehiclesLocations + apiKey
if !vehicles.isEmpty {
for (id , _) in vehicles {
vehicles[id]?.lastPosition = vehicles[id]?.coordinates
}
}
Alamofire.request(.GET, URL)
.validate()
.responseJSON {
(request, response, result) in
guard result.isSuccess else {
print("Error while fetching \(result.error!)")
return
}
let value = result.value as? [String: AnyObject]
let responseDict = value?["response"] as? [String: AnyObject]
let array = responseDict?["entity"] as? [[String: AnyObject]]
for var i = 0; i < array?.count; i++ {
let item = array?[i]["vehicle"] as? [String: AnyObject]
let position = item?["position"] as? [String: AnyObject]
let trip = item?["trip"] as? [String: AnyObject]
let vehicle = Vehicle()
vehicle.latitude = position?["latitude"] as! Double
vehicle.longitude = position?["longitude"] as! Double
vehicle.tripId = trip?["trip_id"] as! String
let startTime = trip?["start_time"] as! String
vehicle.startTime = self.dateFormatter.dateFromString(startTime)
let vehicleId = item?["vehicle"] as? [String: AnyObject]
let id = vehicleId?["id"] as! String
if self.vehicles[id] == nil {
self.vehicles[id] = vehicle
}
else {
self.vehicles[id]?.latitude = vehicle.latitude
self.vehicles[id]?.longitude = vehicle.longitude
}
}
self.createVehicles()
}
}
func fetchPointsForTripPolyline() {
let URL = urlTripPolyline + apiKey
Alamofire.request(.GET, URL)
.validate()
.responseJSON {
(request, response, result) in
guard result.isSuccess else {
print("Error while fetching \(result.error!)")
return
}
let value = result.value as! [String: AnyObject]
let responseArray = value["response"] as! [[String: AnyObject]]
for var i = 0; i < responseArray.count; i++ {
let longitude = responseArray[i]["shape_pt_lon"] as! CLLocationDegrees
let latitude = responseArray[i]["shape_pt_lat"] as! CLLocationDegrees
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
self.points.append(coordinate)
}
self.drawPolyline()
}
}
}
extension MapViewController: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let newLocation = locations.last!
if newLocation.timestamp.timeIntervalSinceNow < -5 {
return
}
if newLocation.horizontalAccuracy < 0 {
return
}
if newLocation.horizontalAccuracy <= locationManager.desiredAccuracy {
stopLocationManager()
currentLocation = newLocation
let annotation = MKPointAnnotation()
annotation.coordinate = newLocation.coordinate
mapView.addAnnotation(annotation)
}
let center = CLLocationCoordinate2D(latitude: newLocation.coordinate.latitude, longitude: newLocation.coordinate.longitude)
let region = MKCoordinateRegionMakeWithDistance(center, 1000, 1000)
self.mapView.setRegion(region, animated: true)
}
}
extension MapViewController: MKMapViewDelegate {
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if !(annotation is BusAnnotation) {
return nil
}
let reuseId = annotation.title!
var busAnnotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId!)
if busAnnotationView == nil {
busAnnotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
busAnnotationView?.canShowCallout = true
}
else {
busAnnotationView?.annotation = annotation
}
let busAnnotation = annotation as! BusAnnotation
busAnnotationView?.image = UIImage(named: busAnnotation.imageName)
return busAnnotationView
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.lineWidth = 3.0
lineView.strokeColor = UIColor.greenColor()
return lineView
}
return MKOverlayRenderer()
}
func hideBuses(tripId: String) {
for (_, vehicle) in vehicles {
if vehicle.tripId != tripId {
let annotationView = mapView.viewForAnnotation(vehicle.annotation!)
annotationView?.hidden = true
}
}
}
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
let vehicleId = view.annotation?.title!
let tripId = vehicles[vehicleId!]?.tripId
hideBuses(tripId!)
}
}

Map not reading code first time in Swift2 iOS9

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.