Tracking Highly Accurate Location in iOS — (Vol.4) Display location on the map

In the last three Medium posts, I’ve explained how to get locations both in the foreground and background with the best accuracy. Before explaining much deeper location topics, I am going to take a time to implement map feature on the sample app.

It is because many location-related apps have a map. And even if your app doesn’t have a map, it often becomes necessary to have a map for debugging and tuning your location tracking feature.

First open Main.storyboard, and add MPMapView on the ViewController’s view

Enable Maps in the project’s Capabilities tab.

Implements MKMapViewDelegate protocol.

And add these member variables.

  • userAnnotationImage — Image of the user’s position annotation.
  • userAnnotationUserAnnotation object to indicate the user’s position. We subclass MKAnnotation and creates this UserAnnotation class for showing custom graphic (the red dot in the screenshot below) on the user’s position.
  • accuracyRangeCircle — MKOverlay class, the overlay showing current accuracy range of the location information (gray circle around the red dot)
    The wider the circle is the lower the accuracy is.
  • polyline- MKPolyline (subclass of MKOverlay) which shows the user’s path on the map (the blue path in the screenshot below.)
  • isZooming — State of zooming action
  • isBlockingAutoZoom — Flag meaning whether it is currently blocking auto zoom due to that the user is scrolling and interacting with the map.
  • zoomBlockingTimerTimer used to block auto zooming for 10 seconds after the user’s interaction to the map ends.
  • didInitialZoom — Boolean flag to store when the map have done initial zoom to the user’s location.

In viewDidLoad, we initialize some of these variables.

  • Set self as the delegate of mapView
  • Disable showsUserLocation of mapView because we use our own custom graphic for the indicator
  • Initialize the image of the user’s current location annotation
  • Set the accuracy range circle with initial dummy location and range width of 50 meters
  • add the accuracy range circle overlay to the map
  • Add an observer to monitor location update notification sent from LocationService class. updateMap method will be called every time new location is obtained.

Next we prepare this updateMap method.

This method calls two methods;

  • updatePolyline()
  • zoomTo(location: newLocation)

updatePolyline() draws the path that the user took by connecting all the tracked locations.

zoomTo() controls the zoom level and position of the map to make the current location of the user visible at the center of the map.

To generate a polyline, we first makes the array of CLLocationCoordinate2D objects from the locationDataArray in LocationService class. locationDataArray is the array storing all the tracked locations.

Then we passes this array to create a MKPolyline object.

zoomTo() is a little complicated.

  • Zoom if the map has never been zoomed to the position of the user’s location.
  • Move the map to the position of the user’s location if zoom is not disabled (i.e. isBlockingAutoZoom is false).
  • Add accuracyRangeCircle overlay — gray circle indicating the current GPS accuracy by the radius of the circle (smaller radius means better accuracy).
  • Add userAnnotation — the red dot indicator of the user’s current position

Zoom is disabled (i.e. isBlockingAutoZoom is set to true) when the user starts touching the map such as pinching or swiping the map. The app keeps isBlockingAutoZoom true for 10 seconds after the user’s last interaction to the map.
By doing that, the app can avoid auto-focusing the map to the user’s latest location while the user is interacting with the map by himself.
This small trick gives much better UX to users especially if the app is running or navigation type of the app.

Override MKMapViewDelegate methods

Provide a renderer to draw an accuracy region and a polyline.

This delegate method provides one renderer object for drawing accuracyRangeCircle, and one renderer object for drawing the polyline.

Provide an annotation view object for the user’s current position annotation

Here you have to return MKAnnotationView object wrapping the annotation object. Since we added userAnnotation to mapView, it is that userAnnotation object which is passed in this function. We wrap this object in the annotationView object here.

React to the change of the map’s rendering region

This method will be called when the region of the map rendered on the screen is changed. This happens in these two cases

  • When a user pinches or scrolls the map
  • When the ViewController zooms the map by zoomTo() function.

We set isZooming flag to false when the user scrolls or pinches the map

We set isZooming flag to true when the ViewController zooms the map.

So, in case isZomming is false, it means the map has been moved by the user, thus we turn isBlockingAutoZoom to true to block the ViewController from doing any zoom. At the same time we start a timer of 10 seconds, and until this timer expires we keep this flag as true. After the 10 seconds, isBlockingAutoZoom is set to false, so the ViewController can zoom or move the map when new location is sent from the LocationService object.

We assume that when and slightly after the user has touched the map, he or she wants to look at the map for a while at that position and zoom level. Thus we block the map from any auto zoom. We assume that after 10 seconds, the user may be comfortable if the map resumes to move focus to the user’s latest location automatically each time new location of the user is obtained.

Next post — How to filter inaccurate locations

If you need any dev help..

I’m running an app development studio in Tokyo called Goldrush Computing.
If you need any development force to empower your app with location tech magic, feel free to contact me any time.
mizutori@goldrushcomputing.com

--

--

Taka Mizutori
How to track user’s location with high accuracy (iOS / iPhone)

Founder and CEO of Goldrush Computing Inc (https://goldrushcomputing.com). Keep making with Swift, Kotlin, Java, C, Obj-C, C#, Python, JS, and Assembly.