Silver Challenge User's location


#1

Ok here we go:

1: Add to info.plist the key: Privacy - Location When In Use Usage Description

2: Add delegates:

 class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate 

3: Add in loadView()

    mapView.delegate = self
    locationManager = CLLocationManager()

4: Create a function in MapViewController class for build and add button to MKViewMap, this have an UIView parameter, this is intended for be as a reference for our magical button

func initLocalizationButton(_ anyView: UIView!){
    let localizationButton = UIButton.init(type: .system)
    localizationButton.setTitle("Localization", for: .normal)
    localizationButton.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(localizationButton)
    
    //Constraints
    
    let topConstraint = localizationButton.topAnchor.constraint(equalTo:anyView
        .topAnchor, constant: 32 )
    let leadingConstraint = localizationButton.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
    let trailingConstraint = localizationButton.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor)
    
    topConstraint.isActive = true
    leadingConstraint.isActive = true
    trailingConstraint.isActive = true
    
    localizationButton.addTarget(self, action: #selector(MapViewController.showLocalization(sender:)), for: .touchUpInside)
    
    
}

5: The function above pops an error, so add the next functions in the same class

func showLocalization(sender: UIButton!){
    locationManager.requestWhenInUseAuthorization()//se agrega permiso en info.plist
    mapView.showsUserLocation = true //fire up the method mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation)

    
}

func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
    //This is a method from MKMapViewDelegate, fires up when the user`s location changes
    let zoomedInCurrentLocation = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 500, 500)
    mapView.setRegion(zoomedInCurrentLocation, animated: true)
}

6: At last but not least , add function initLocalizationButton at the bottom of loadView method

initLocalizationButton(segmentControl)

7: Run your project! Done! :smiley:


#2

Alternatively, instead of adding

mapView.delegate = self

and then

func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation)

you can just place the following in the showLocation method:

mapView.userTrackingMode = .follow

The user tracking mode will automatically show the user’s location and zoom into the region


#3

Thanks for the solution. I had trouble creating the button. For the button action I don’t know why mapView.showsUserLocation does not fire the delegate function every time. This worked for me instead.

func showLocalization(sender: UIButton!) {
        locationManager.requestWhenInUseAuthorization()
        self.mapView(mapView, didUpdate: location)
    }

#4

Excellent, your solutions are great, thank you!


#5

I’ve tried thousands of times. My code is right but nothing happens when I press the button. When I write the code for the first time and run the simulator, it asks me the request (but after that the button does nothing). When I run simulator again, it even doesn’t show the message. I’m totally mad because I have checked anything including adding CoreLibrary framework, and text in info.plist. Does anybody have any ideas what can be wrong?


#6

Hi friends,
I am stuck on this Silver challenge.
I got the following error and I am not sure why. I tried adding “var” in front of locationManager = CLLocationManager(), but nothing works.

Here is my code:


#7

It is much better to post the actual code itself instead of just a screen shot.

Also when posting code, make sure that it is properly formatted, and you place the code between a pair of three back tick characters.

For example, if you type this:
```
// print something
print (“Two is an even prime.”)
print (“Not only that, it is the largest even prime.”)

```
You will see this:

// print something
print ("Two is an even prime.")
print ("Not only that, it is the largest even prime.")


#8
//
//  MapViewController.swift
//  WorldTrotter
//
//  Created by Timothy Huang on 3/21/17.
//  Copyright © 2017 Timothy Huang. All rights reserved.
//

import UIKit
import MapKit

class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate
//class MapViewController :UIViewController
{

    // Variable
    var mapView : MKMapView!
    
    // If a view controller is asked for its view and its view is nil, then the loadView() method is called
    override func loadView() {
        
        // Silver Challenge
        mapView.delegate = self
        
        locationManager = CLLocationManager()
        
        
        // Create a map view
        mapView = MKMapView()
        
        // Set it as the view of this view controller
        view = mapView
        
      
        // Programmatic constraints
        // A segmented control allows the user to choose between a discrete set of options
        // and you will use of to allow the user to switch between map types: standard, hybrid, and satellite
        let segmentedControl = UISegmentedControl(items:["Standard", "Hybrid", "Satellite"])
        segmentedControl.backgroundColor = UIColor.white.withAlphaComponent(0.5)
        segmentedControl.selectedSegmentIndex = 0
        segmentedControl.addTarget(self, action: #selector(MapViewController.mapTypeChanged(_:)), for: .valueChanged)
        segmentedControl.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(segmentedControl)
    
        let topConstraint = segmentedControl.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 8)
        
        // What is a margin? 
        let margins = view.layoutMarginsGuide
        let leadingConstraint = segmentedControl.leadingAnchor.constraint(equalTo: margins.leadingAnchor)
        let trailingConstraint = segmentedControl.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
        
        topConstraint.isActive = true
        leadingConstraint.isActive = true
        trailingConstraint.isActive = true
        
    } // end loadView()
    
    // This method will check which segment was selected and update the map accordingly
    func mapTypeChanged(_ segControl: UISegmentedControl) {
        switch segControl.selectedSegmentIndex{
        case 0:
            mapView.mapType = .standard
        case 1:
            mapView.mapType = .hybrid
        case 2:
            mapView.mapType = .satellite
        default:
            break
        }
    }
    // viewDidLoad gets called after the view controller's interface file is loaded
    // this method is called just before a view controller's view is added to the window
    override func viewDidLoad() {
        super.viewDidLoad()
        print("MapViewController loaded its view.")
        
    }
    
    // viewWillAppear gets called each time the view controller's view appears onscreen
    override func viewWillAppear(_ animated: Bool) {
        print("viewWillApear MapViewController loaded its view.")
    }

    /**
     Silver challenge
     
     What does this method do? 
    **/
    func initLocalizationButton(_ anyView: UIView!){
        let localizationButton = UIButton.init(type: .system)
        localizationButton.setTitle("Localization", for: .normal)
        localizationButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(localizationButton)
        
        //Constraints
        
        let topConstraint = localizationButton.topAnchor.constraint(equalTo:anyView
            .topAnchor, constant: 32 )
        let leadingConstraint = localizationButton.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
        let trailingConstraint = localizationButton.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor)
        
        topConstraint.isActive = true
        leadingConstraint.isActive = true
        trailingConstraint.isActive = true
        
        localizationButton.addTarget(self, action: #selector(MapViewController.showLocalization(sender:)), for: .touchUpInside)

    } // end 1

    // ?
    func showLocalization(sender: UIButton!){
        locationManager.requestWhenInUseAuthorization()//se agrega permiso en info.plist
        mapView.showsUserLocation = true //fire up the method mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation)
    }

    // ?
    func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
        //This is a method from MKMapViewDelegate, fires up when the user`s location changes
        let zoomedInCurrentLocation = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 500, 500)
        mapView.setRegion(zoomedInCurrentLocation, animated: true)
        //mapView.userTrackingMode = .follow  
    }

} //end class


#9

dicarlomagnus, Just out of curiosity , what crumb trail did you follow to figure out a info.plist key was needed for this challenge?

Well, think I answered this on my own trying to do it without having an info.plist entry.

I have this in the console, which is the beginning of a bread crumb trail for me:

2017-03-31 11:38:32.121700-0400 WorldTrotter[2293:623583] This app has attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSLocationWhenInUseUsageDescription key with a string value explaining to the user how the app uses this data

Weird I’ve added the Info.plist entry “Privacy - Location When In Use Usage Description”, with a string value describing that I’m showing the user’s location, but the app compiles and still gives the same error even after saving the plist file and deleting the app off my phone prior to trying it.

2017-03-31 13:05:04.244882-0400 WorldTrotter[2372:648856] This app has attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSLocationWhenInUseUsageDescription key with a string value explaining to the user how the app uses this data

Is there some other step that clears this besides adding the key/value string to Info.plist?
note: pasting in a key name as “NSLocationWhenInUseUsageDescription” just converts immediately to the text indicated below for the key name.

FInal Update I hope: I deleted the privacy key and readded it, and that seems to have fixed it.
Hope this may help someone else.


#10

uuwen: In your implementation how do you define “location”?


#11

I’m having the same issue as others, where nothing happens when I press the Localization button. Any help would be much appreciated. My code is as follows:

import UIKit
import MapKit

class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
    
    var mapView: MKMapView!
    var locationManager: CLLocationManager?
    
    override func loadView() {
        // Create a map view
        mapView = MKMapView()
        mapView.delegate = self
        locationManager = CLLocationManager()
        locationManager!.delegate = self
        
        // Set it as *the* view of this view controller
        view = mapView
        
        let segmentedControl = UISegmentedControl(items: ["Standard", "Hybrid", "Satellite"])
        segmentedControl.backgroundColor = UIColor.white.withAlphaComponent(0.5)
        segmentedControl.selectedSegmentIndex = 0
        
        segmentedControl.addTarget(self, action: #selector(MapViewController.mapTypeChanged(_:)), for: .valueChanged)
        
        segmentedControl.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(segmentedControl)
        
        let topConstraint = segmentedControl.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 8)
        
        let margins = view.layoutMarginsGuide
        let leadingConstraint = segmentedControl.leadingAnchor.constraint(equalTo: margins.leadingAnchor)
        let trailingConstraint = segmentedControl.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
        
        topConstraint.isActive = true
        leadingConstraint.isActive = true
        trailingConstraint.isActive = true
        
        initLocalizationButton(segmentedControl)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print("MapViewController loaded its view.")
    }
    
    func mapTypeChanged(_ segControl: UISegmentedControl) {
        switch segControl.selectedSegmentIndex {
        case 0:
            mapView.mapType = .standard
        case 1:
            mapView.mapType = .hybrid
        case 2:
            mapView.mapType = .satellite
        default:
            break
        }
    }
    
    func initLocalizationButton(_ anyView: UIView!){
        let localizationButton = UIButton.init(type: .system)
        localizationButton.setTitle("Localization", for: .normal)
        localizationButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(localizationButton)
        
        //Constraints
        let topConstraint = localizationButton.topAnchor.constraint(equalTo:anyView
            .topAnchor, constant: 32 )
        let leadingConstraint = localizationButton.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
        let trailingConstraint = localizationButton.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor)
        
        topConstraint.isActive = true
        leadingConstraint.isActive = true
        trailingConstraint.isActive = true
        
        localizationButton.addTarget(self, action: #selector(MapViewController.showLocalization(sender:)), for: .touchUpInside)
    }
    
    func showLocalization(sender: UIButton!){
        locationManager!.requestWhenInUseAuthorization()//se agrega permiso en info.plist
        mapView.showsUserLocation = true //fire up the method mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation)
        mapView.userTrackingMode = .follow
    }
    
//    func showLocalization(sender: UIButton!) {
//        locationManager?.requestWhenInUseAuthorization()
//        self.mapView(mapView, didUpdate: location)
//    }
    
    func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
        //This is a method from MKMapViewDelegate, fires up when the user`s location changes
        let zoomedInCurrentLocation = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 500, 500)
        mapView.setRegion(zoomedInCurrentLocation, animated: true)
    }
}

#12

I figured out my problem, hopefully this may help others as well: you have to set what the location of the simulator actually is. You do this by editing the scheme and selecting a location, similarly to how you change the language and international number formatting.


#13

Same problem happened to me. It seems that mapView.showUserLocation = true is not triggering the given method. I recommend use mapView.userTrackingMode = .follow it works great


#14

I figured out my problem, hopefully this may help others as well: you have to set what the location of the simulator actually is. You do this by editing the scheme and selecting a location, similarly to how you change the language and international number formatting.

Hmm, what screen should I be looking at for this option?


#15

Search for “Edit Scheme” in the help menu


#16

Great, thanks tonedabass.

So under the default settings/conditions of the project, only a few major cities can be selected right? When I click localization, it will go to the default location that I selected: Sydney, Australia; San Francisco, CA, etc. so I don’t see it zoom on my current location.

I see an option to add a GPX file to the project for additional cities.


#17

To simulate location in xcode 8 you can also simply click on Debug -> Simulate location and click on one of the cities closest to you.


#18

I get an error that says locationManager is an unresolved identifier. Any ideas what is going on there?


#19

Your constraints for the button aren’t correct. It causes the app to crash.