Photo by delfi de la Rua on Unsplash

Core Location — Integration with MapKit

Andrew Lundy
Level Up Coding
Published in
5 min readOct 12, 2020

--

Exordium

In this post, I’ll be taking you through the process of integrating Core Location and MapKit. We will be piggy-backing off of the series started here. You can find the code to this tutorial on GitHub.

Core Location is a part of Cocoa Touch — and is the framework used for obtaining user location details. The location details can be used in many different ways — and how you use this information will depend on your app’s functionality. Whether you’re building a food delivery app, concert booking system, or a tool to find the nearest bathroom — Core Location is going to be a key component to your product. In this case, the user location will be displayed in the middle of a view controller that hosts a map.

Let’s begin.

Adding the Map

Start with the mapkit branch of the repo mentioned above. You will see a new button in the ViewController and a new view controller named MapViewController. (I have added both of these components for you, as you can see when compared to the geocoder branch).

The first thing to do in the MapViewController is to add the map view, which will be an MKMapView object. Add this property to the MapViewController class:

private var mapView: MKMapView!

The next thing to do is create the layout of the MapViewController. The ViewController class is connected to the Main.storyboardfile, but I am going to build the MapViewController programmatically. When building view controller interfaces with code, I like to keep all of the UI work in a setupView method that ends up calling another method that applies all of the Auto Layout constraints.

Here is the simple setup of the MapViewController:

import UIKit
import CoreLocation
import MapKit

class MapViewController: UIViewController {

// MARK: - Properties
private var mapView: MKMapView!

// MARK: - Methods

// 1. Complete any user interface work.
func setupView() {
mapView = MKMapView()
mapView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(mapView)

applyAutoConstraints()
}

// 2. Complete any Auto Layout work.
func applyAutoConstraints() {
NSLayoutConstraint.activate([
mapView.topAnchor.constraint(equalTo: view.topAnchor),
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}


// MARK: - Overrides
override func viewDidLoad() {
super.viewDidLoad()

// 3. Call the method that sets up the user interface.
setupView()
}
}

Here, I have imported Core Location and MapKit. The first thing to do after importing the frameworks is to set up the user interface.

1.) The setupView method will hold any UI work that involves setting up components and their attributes. In this case, we instantiate the MKMapView object and set it's translatesAutoresizingMaskIntoConstraints to false. Setting this property to false lets the UI component be affected by Auto Layout. As stated in Apple's docs 'If you want to use Auto Layout to dynamically calculate the size and position of your view, you must set this property to false, and then provide a non ambiguous, nonconflicting set of constraints for the view.' Only objects that inherit from UIView will have access to the translatesAutoresizingMaskIntoConstraints property. The final thing to do with the mapView object is to add it to the view as a subview.

2.) Any Auto Layout work that needs to be done for the UI components will be done in the applyAutoConstraints method. Here, mapViewwill fill the entire screen since the anchors are all equal to the view's corresponding anchors.

3.) To get any of this code to do anything, call the setupView method in the viewDidLoad method.

The last bit of UI set up to do is adding a title and cancel button to the navigation bar of the MapViewController. Add the following to the beginning of the viewDidLoad method, before the call to setupView.

title = "View Your Location"
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(dismissToMainView))

The dismissToMainView selector is an Objective-C selector hosted in the MapViewController. It simply dismisses the current view controller.

@objc func dismissToMainView() {
dismiss(animated: true, completion: nil)
}

Passing the User’s Location

To get the user’s location to display on the map, it must be passed from the ViewController to the MapViewController. Right now, the MapViewController does not contain a property where we can store the location - so add one with a default internal access level.

var userLocation: CLLocation!

Then, in the ViewController, class locate the IBAction named viewLocationOnMapButtonPressed and:

1.) Create an instance of MapViewController.
2.) Pass the current location to the mapViewController's userLocation property.
3.) Embed the mapViewController in a UINavigationController and present the navigationController.

@IBAction func viewLocationOnMapButtonPressed(_ sender: Any) {
// 1
let mapViewController = MapViewController()
// 2
mapViewController.userLocation = currentLocation
// 3
let navigationController = UINavigationController(rootViewController: mapViewController)
present(navigationController, animated: true, completion: nil)
}

Display the User’s Location on the Map

Before the location is displayed on the map:
1.) The showsUserLocation property of the map must be set to true.
2.) The delegate will be set to self (make sure the MapViewController conforms to the MKMapViewDelegate).
3.) The region will be based on the user's location that is passed in from the ViewController. The region of a map is the part of the map that is currently displayed on the screen. It is of the type MKCoordinateRegion, which will be created with the initializer that contains the center and span parameters. The center will be the user's coordinate, and the span is of the type MKCoordinateSpan. This property defines the height and width of the region. Lower delta values will increase the zoom level on the map.

The setUpView method should now look like this:

func setupView() {
mapView = MKMapView()
mapView.translatesAutoresizingMaskIntoConstraints = false

// 1
mapView.showsUserLocation = true
// 2
mapView.delegate = self
// 3
mapView.region = MKCoordinateRegion(center: userLocation.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))

view.addSubview(mapView)
applyAutoConstraints()
}

The last thing to do is implement the regionDidChangeAnimated method in the MKMapViewDelegate and set the map's region after the region has been updated. This is not necessary, but it helps re-center the map when the user scrolls away from their location with their finger.

extension MapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
mapView.setRegion(MKCoordinateRegion(center: userLocation.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)), animated: true)
}
}

If the app is run, the location is obtained, and the ‘View Location on Map’ button is tapped — the MapViewController will be presented, and you will see your location in the center of the map. There is much more you can do with MapKit, and this blog post really just touches the basics of what MapKit is and how it can be used.

Core Location integrates nicely with MapKit, and is a key component to any location-based iOS app. Core Location also gives developers the ability to reverse-geocode GPS coordinates and create human-readable addresses. Learn more about reverse-geocoding with Core Location on our blog.

You can read more articles related to iOS Development, Freelancing, Software Engineering and more at rustynailsoftware.com. Feel free to connect on Twitter. Or, get in touch if you’re looking for a dependable iOS Developer for your next project.

--

--