The code below works great but once you try to scroll around on the map or anywhere away from the user location, it pulls it right back and centers back on the user location. It won't release it.
How would you let it release after it centers in on the user's location after it loads?
import MapKit
import CoreLocation
class MapView : UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var map: MKMapView!
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled()) {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
}
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.map.setRegion(region, animated: true)
}
Related
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapKitView: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
mapKitView.delegate = self
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = CLLocationCoordinate2D(latitude: locations[0].coordinate.latitude, longitude: locations[0].coordinate.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0.001, longitudeDelta: 0.001)
let region = MKCoordinateRegion(center: location, span: span)
mapKitView.setRegion(region, animated: true)
}
}
mapKitView.setRegion(region, animated: true) is blocking User Interaction in mapkit and I can't zooming, scrolling and rotating
It seems from the code that you are trying to keep the map centered on the user's location.
To do this while preserving user interaction it's better to delete the line:
mapKitView.setRegion(region, animated: true)
and instead set the userTrachingMode property on mapKitView to .follow or .followWithHeading.
with this, the map's visible region will follow the user as the location updates.
Here's the documentation of the userTrackingMode property on MKMapView: https://developer.apple.com/documentation/mapkit/mkmapview/1616208-usertrackingmode
I am trying to get and display the user's location, but the simulator will only load the country I am in. I set up the required info.plist sections (privacy - location when in use and always), linked the mapView in Main.storyoard to the delegate (control-dragged to View Controller Icon), attached the IBOutlet (checked it to make sure it is properly connected) and wrote this code in viewController.swift:
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
mapView.mapType = MKMapType.standard
let location = locations[0]
let myLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: myLocation, span: span)
mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
}
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am just getting my feet wet with MapKit and this issue has been keeping me back for quite some time. Would appreciate any help.
Please use iPhone Device to get the current location. In the simulator, you have to set location then get that location. So can do this easily using the device. Your code will work with the real device.
If you want to test it with simulator the follow https://medium.com/#abhimuralidharan/location-simulation-in-xcode-ff7db9042710
I don't believe this question has been asked yet in Swift 3.0 - three goals:
Upon viewDidLoad the Map Centers to the User Location at a certain zoom level that can be set (for example let span: MKCoordinateSpan = MKCoordinateSpanMake(40.0, 40.0))
Once the map loads and centers on the User Location, the User can then move and scroll the map to any other location WITHOUT the map automatically snapping back to the original User Location
Allow the User to ONLY zoom in to a certain level but allow the User to zoom out fully to view the entire global map (no restrictions on the zoom out level)
Here is my code thus far:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let span: MKCoordinateSpan = MKCoordinateSpanMake(40.0, 40.0)
let userLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region: MKCoordinateRegion = MKCoordinateRegionMake(userLocation, span)
mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
1. Should work with the code you have now.
2. Add check for subsequent location updates
In the didUpdateLocations method, add a Bool to check whether the region was centered on the user already or not.
var regionHasBeenCentered = false
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
if !regionHasBeenCentered {
let span: MKCoordinateSpan = MKCoordinateSpanMake(40.0, 40.0)
let userLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region: MKCoordinateRegion = MKCoordinateRegionMake(userLocation, span)
mapView.setRegion(region, animated: true)
regionHasBeenCentered = true
}
self.mapView.showsUserLocation = true
}
Now the map will no longer center on the user after the first update, until you change regionHasBeenCentered back to false. This will allow the user to scroll and zoom freely.
3. Implement MKMapViewDelegate method to detect map region changes
Implement MKMapViewDelegate on your view controller so that you can check for region changes.
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
…and set the view controller as the delegate:
override func viewDidLoad() {
// other things…
mapView.delegate = self
}
Then implement the following method which will be called right before the region changes. Here you can check to see if the span's dimensions are too small, and set them to a minimum appropriate.
func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
if mapView.region.span.latitudeDelta <= 40 && mapView.region.span.longitudeDelta <= 40 {
let minimumSpan = MKCoordinateSpan(latitudeDelta: 40, longitudeDelta: 40)
let minimumRegion= MKCoordinateRegion(center: mapView.centerCoordinate, span: minimumSpan)
mapView.setRegion(minimumRegion, animated: false)
}
}
Important note: From the MKCoordinateSpan documentation, the longitudeDelta will change as you move toward/away from the equator.
longitudeDelta
The amount of east-to-west distance (measured in degrees) to display for the map region. The number of kilometers spanned by a longitude range varies based on the current latitude. For example, one degree of longitude spans a distance of approximately 111 kilometers (69 miles) at the equator but shrinks to 0 kilometers at the poles.
Furthermore, MKCoordinateSpan's dimensions are measured in degrees, and 40 degrees is quite a bit so you probably want to change these values, otherwise the user will not be able to zoom in much at all.
I have been able to implement MapKit into my app, but it currently looks the map in one static position.
I have been struggling to add the pinch and zoom functionality as well as the ability to allow users to explore other regions on the map.
Here is what my current code looks like:
import UIKit
import MapKit
import CoreLocation
class HotPlacesViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var locationManager = CLLocationManager()
let newPin = MKPointAnnotation()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Setup the location services delegate in this class.
locationManager.delegate = self
// This little method requests the users permission for location services whilst in this view controller.
if CLLocationManager.authorizationStatus() == .notDetermined {
self.locationManager.requestAlwaysAuthorization()
let alert = UIAlertController(title: "You can change this option in the Settings App", message: "So keep calm your selection is not permanent. 🙂",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
// Drops the pin on the users current location.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
mapView.removeAnnotation(newPin)
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))
// Set the region on the map.
mapView.setRegion(region, animated: true)
newPin.coordinate = location.coordinate
mapView.addAnnotation(newPin)
}
}
I think that you problem is related to this method func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]), because over and over again when the position is updated you are adjusting the region in the map that is why you see only the region where the position is updated and can't move any other region in the map, you must make a flag for first time and adjust the region only once or don't adjust the region at all
EDITED
import UIKit
import MapKit
import CoreLocation
class HotPlacesViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var isFirstTime = true
var locationManager = CLLocationManager()
let newPin = MKPointAnnotation()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Setup the location services delegate in this class.
locationManager.delegate = self
// This little method requests the users permission for location services whilst in this view controller.
if CLLocationManager.authorizationStatus() == .notDetermined {
self.locationManager.requestAlwaysAuthorization()
let alert = UIAlertController(title: "You can change this option in the Settings App", message: "So keep calm your selection is not permanent. 🙂",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
// Drops the pin on the users current location.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
mapView.removeAnnotation(newPin)
let location = locations.last! as CLLocation
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
if(self.isFirstTime){
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
// Set the region on the map.
mapView.setRegion(region, animated: true)
self.isFirstTime = false
}
newPin.coordinate = location.coordinate
mapView.addAnnotation(newPin)
}
}
Hope this helps, and let me know if I misunderstood your question
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.setRegion(region, animated: true)
Set span for zoom in MapKit
I have successfully made an app that shows a blinking blue dot where the user currently is. However as of right now the center of the screen does not follow the blue dot as the user moves, so if the user moves the blue dot just exits the screen and the user would have to scroll to keep up with it. That isnt user friendly! I have the below code:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var map: MKMapView!
let locationManager = CLLocationManager()
var mapp = MKMapView.self
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if (CLLocationManager.locationServicesEnabled())
{
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.map.showsUserLocation = true
}
else
{
print("Location services are not enabled")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002))
self.map.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()
}
}
My only thoughts are that I could get rid of the "stopUpdatingLocation() section and maybe then it would continuously update the region with the new center for every new location, however I am not sure if not having the stopUpdatingLocation is bad practice? Thanks for all advice!
Use userTrackningMode which causes the map view to center the map on that location and begin tracking the user’s location. If the map is zoomed out, the map view automatically zooms in on the user’s location, effectively changing the current visible region.
To use it
mapView.setUserTrackingMode(.follow, animated:true)