update center of screen with current location - ios

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)

Related

Swift 4, Xcode 9: Getting and displaying the user's location

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

iOS MKMapView Center on User Location and then allow unlimited Scrolling in Swift 3.0

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.

Can't display user current location Mapbox iOS API

I want to display user's current location immediately after they open the app. I use Mapbox iOS SDK. But it doesn't work at all. I don't know what's wrong. I can only see an irrelevant map after I open it.
import UIKit
import Mapbox
import CoreLocation
class ViewController: UIViewController, MGLMapViewDelegate, CLLocationManagerDelegate
{
#IBOutlet weak var mapView: MGLMapView!
let locationManager = CLLocationManager()
override func viewDidLoad()
{
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: Location Delegate Methods
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last
let mapView = MGLMapView(frame: view.bounds)
mapView.userTrackingMode = .Follow
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
mapView.setCenterCoordinate(center, zoomLevel: 15, animated: true)
view.addSubview(mapView)
self.locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
print ("Errors:" + error.localizedDescription)
}
}
I think the issue might be the iOS Simulator. When you run an app on the simulator, it doesn't simulate your location until you tell it to.
If you look at your Xcode, you'll see a navigate button at the bottom.
Try changing the location to something, and it should show up :)

Swift - map returns to starting point

I have a map in my app but when i move the map around, the second I let go, it returns to the starting point. I do not want this to happen..
My ViewController looks like this atm:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var map: MKMapView!
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
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)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
CLLocationManager's startUpdatingLocation sends updates to the User's location to locationManager:didUpdateLocations:.
Calling this method causes the location manager to obtain an initial location fix (which may take several seconds) and notify your delegate by calling its locationManager:didUpdateLocations: method.
So then in locationManager:didUpdateLocations: the region you're moving the map to with setRegion:animated: is the location that just got sent, i.e., the User's location.
You set the region of your mapView in the didUpdateLocations delegate method.
Once you start to call startUpdatingLocation(), it will start to keep retrieving your location coordinate, and your didUpdateLocations method will be called, and your mapView will be update by the setRegion() in your method.
You can change the value of location manager's distanceFilter to a bigger number, so it will update less frequently. By default, kCLDistanceFilterNone is used, so your didUpdateLocations will be called of all movements.
So it seems like I just had to add locationManager.stopUpdatingLocation() to the didUpdateLocations function

Swift Mapkit - Move Away From User Location

I want to display user location and the surrounding area, but I also want to allow the user to pan around the area. Right now if I try to scroll somewhere else on the map it automatically takes me back to the base region with the user at the center. How do I stop this? I want to show the initial view with the user in the center, but I want to be able to scroll around too. Thanks in advance you guys are so helpful!
import UIKit
import MapKit
import CoreLocation
class ViewControllerMain: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var locationManager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.startUpdatingLocation()
mapView.showsUserLocation = true
mapView.delegate = self
let longPress = UILongPressGestureRecognizer(target: self, action: "action:")
longPress.minimumPressDuration = 1.0
mapView.addGestureRecognizer(longPress)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
let regionToZoom = MKCoordinateRegionMake(manager.location.coordinate, MKCoordinateSpanMake(0.01, 0.01))
mapView.setRegion(regionToZoom, animated: true)
}
Your code in didUpdateLocations is resetting the region. You have two options.
Store in an ivar whether or not you have already set the first location. Only if you haven't do you then set the region.
Set a timer that runs for 15 seconds. If the map is moved by the user you reset the timer. When the timer expires you can recenter to the users location.
This will keep the map centred around the user but will enable them to pan around a bit to get some context.
This answer shows how to do it in Objective-C
locationManager.stopUpdatingLocation();
this is all you need at end of locationManager fun, if you are still looking for
This thread had a good example in both Swift and Obj C. Be sure to look for the comment on the answer I've linked to if you use Swift.
After you set that up, use Control Flow within the didUpdateLocations, so that it re-centers the user's location only if the user has not touched the map.
Here is my full code as an example:
#IBOutlet weak var theMap: MKMapView!
// ...
// This var and the three following functions are used to tell if the map moves because of the user.
// This is used in the control flow in didUpdateLocations
private var mapChangedFromUserInteraction = false
private func mapViewRegionDidChangeFromUserInteraction() -> Bool {
let view: UIView = self.theMap.subviews[0] as UIView
// Look through gesture recognizers to determine whether this region change is from user interaction
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
if( recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Ended ) {
return true
}
}
}
return false
}
func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
mapChangedFromUserInteraction = mapViewRegionDidChangeFromUserInteraction()
if (mapChangedFromUserInteraction) {
// user changed map region
println("user changed map region")
}
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if (mapChangedFromUserInteraction) {
// user changed map region
println("user changed map region")
}
}
// This function is called each time the user moves.
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
// Use Control Flow: if the user has moved the map, then don't re-center.
// NOTE: this is using 'mapChangedFromUserInteraction' from above.
if mapChangedFromUserInteraction == true {
// do nothing, because the user has moved the map.
}
else {
// update on location to re-center on the user.
// set X and Y distances for the span (zoom). This is very zoomed in.
let spanX = 0.0005
let spanY = 0.0005
// Create a region using the user's location, and the zoo.
var newRegion = MKCoordinateRegion(center: theMap.userLocation.coordinate, span: MKCoordinateSpanMake(spanX, spanY))
// set the map to the new region
theMap.setRegion(newRegion, animated: true)
}
}

Resources