How to create a Map View in iOS (swift 4)

Map View




MKMapView and MKMapViewDelegate Introduction in Swift


If you need to show a map to your users in iOS, one way to accomplish this is to use the MKMapView class. Mix this with the MKMapViewDelegate and you have the ability to detect movement on the map, detect the state of the map such as failure loading it as well as several other items you can monitor.
In this tutorial we’ll add a map to the view, allow the current location of the phone to be zoomed to, and allow the user to switch between the different map types which are available meaning Standard, Satellite, and Hybrid. We will also learn how to implement a delegate method and have the map move each time the user moves.

MKMapView Tutorial

The first step is to create a Single View Application by opening Xcode and creating the new project. You can follow this tutorial if you are unsure how to create a new project. Make sure you select the language as Swift. You can uncheck unit tests, core data, and other optional extras.
Navigate to Main.storyboard and drag out a UIToolBar and seat it at the bottom of the view. Click the UIBarButtonItem (named Item) and rename the text attribute to “Current Location”. Drag out a Segment control and put that on the toolbar also. You might choose to use a flexible spacer between the button on the left and the segment to force them to the left and right edges of the toolbar.
segment-attributesSelect the Segment View and change the segments to 3. Rename each segment as follows:

Segment 0: Standard
Segment 1: Satellite
Segment 2: Hybrid


The next task is to drag a Map Kit View in to the view. I made mine fill the rest of the view above the toolbar.



When complete you should have something similar to that shown above.
We now need to create some IBOutlet and IBAction properties. Open up the assistant editor which is found on the top toolbar over on the far right just (second set of controls from the right). Click the middle button which is 2 intersecting circles.
Ctrl+drag from the MapView to just below the class declaration to create an IBOutlet. Call it mapView and hit connect.


outletDo the same from the current location and segment control. I dragged to just below viewDidLoad. Instead of leaving “outlet” as the default you need to change the connection to “Action”. I also changed the type to UISegmentedControl and gave it the name of mapTypeChanged. Click connect when done.
You can do the same for the Current Location button, but rather than selecting the type as UISegmentedControl (which isn’t available) you can select UIBarButtonItem from the dropdown.

Implementing MapView in the ViewController.swift Class

The storyboard is complete. It’s time to write some code to get the project working.
Step 1:
First we import MapKit as well as CoreLocation. The reason we need to implement CoreLocation is so that we can use it to request authorisation for using the users location.
import MapKit
Create a property for a CLLocationManager as follows (which can be put just below the mapView IBOutlet):


var locationManager = CLLocationManager.init()


In viewDidLoad add the following:


        loactionManager.requestWhenInUseAuthorization()
        mapView.delegate = self
        mapView.mapType = .standard
        mapView.showsCompass = true
        mapView.showsUserLocation = true
        mapView.showsScale = true




Line 1 is where we request from the user permission to use their location. As well as adding this line, you also need to add a key and description to the info.plist file for your app. The key to add is:
NSLocationWhenInUseUsageDescription
You can set the string description as anything you like such as “This app uses location so it can show where you are currently on a map”. Please use something better written if you publish your app.
On the next lines of code we next set the mapType to .standard which is actually already defaulted to this anyway. We also set .showsUserLocation to true as well as a few other items. I added these just to demonstrate what can be enabled or disabled. You can make these changes in the storyboard as well by clicking on the map view and setting attributes that way. You can opt to set both in code as well as in the attributes section in the storyboard. What you set in the storyboard will get overridden by what you set in code.
When you run the app you will now see a map that shows your current location although you might not be able to see that on screen. You will need to scroll around to find the blue dot if your country is out of view. If you are using a simulator make sure you enable location in the Debug menu for the simulator.

Implementing the Current Location Button and the Segmented Control

You should have 2 IBAction methods available in the class. One will be called something like zoomToCurrentLocation: and the other mapTypeChanged:. These were added when you ctrl+dragged from the storyboard earlier.
Lets start with zoomToCurrentLocation:.
To zoom to the users current location we need to create an MKCoordinateSpan and then create an MKCoordinateRegion and then set the region on the mapView.
Line 3 is where we set the region. An MKCoordinateRegion requires a span which we set on line 2 and it also requires the location. For this, we are using the current location taken from the locationManager. MapKit also has a current location property, but in this instance I opted to use the one from locationManager. Note that location is optional on the locationManager, so you might want to check for nil values if you are implementing this in production as it may crash if nothing is provided when it expected something.
On line 4 we set the region and set animated to true meaning that when you zoom to the location it will animate there rather than just appear.
Next we need to implement mapTypeChanged. If you ctrl-dragged from the segmented control and not the UIBarButtonItem that it was embedded in, you should have a sender of type UISegmentedControl. We need to implement the following:
        
      switch sender.selectedSegmentIndex {
         case MapType.StandardMap.rawValue:
            mapView.mapType = .standard
         case MapType.SatelliteMap.rawValue:
            mapView.mapType = .satellite
         case MapType.HybrideMap.rawValue:
            mapView.mapType = .hybrid
         default:
            break
      }
  


I opted to use a switch here and check for the selectedSegmentIndex. At the top of the class I also created an enum with some values so that we can check by a more friendly name rather than just specifying a 0, 1, or a 2. I did this as follows:

enum MapType:NSInteger {
        case StandardMap = 0
        case SatelliteMap = 1
        case HybrideMap = 2
    }


So on the mapTypeChanged method I used the switch to select which segment was selected. I then set the mapType to the appropriate map type selected.
When you test the app now you should be able to zoom to your current location (with a view of just over half a mile), and be able to change the map type by selecting it from the segmented control.

MKMapViewDelegate

It’s time to look at the delegate and see what we can do with that. For this example we will use something simple. If the current location of the user changes, the map will move and centre on that location.
To do this, we need to adopt the MKMapViewDelegate by adding it to the class declaration like so:
class ViewController: UIViewController , MKMapViewDelegate {

None of the delegate methods are required in MKMapViewDelegate which means that no warnings will appear once the delegate is specified. Lets go ahead and implement mapView:didUpdate userLocation as follows:
 func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
    }


A quick way to add delegate methods is to start typing the function name as found in the documentation. We can see that it begins with mapView, so we type mapView and code completion will bring a list of available methods as follows:



code-complete
Highlight the one you want and hit enter. If code completion disappears, move the curser to where you typed and hit escape.
We need to set the delegate to self next. You can do this in viewDidLoad by adding the following line:
                mapView.delegate = self



If you put the following in the delegate method you just added in the step before this, each time you change location (either in the simulator or on a physical device) it will print test to the view although this isn’t immediate and sometimes takes a few seconds which is fine.

             printe("test")



After confirming that the delegate method is being called, you can go ahead and delete the print statement.
We now want to centre the map on the current location of the device automatically using this delegate method:
Add the following to the delegate method:

func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
        mapView.setCenter(userLocation.coordinate, animated: true)
    }


This line uses the setCentre method to move the map to a centre coordinate and animate the transition to that coordinate. Note that it doesn’t zoom the map at this point. The map stays at it’s current span.
If you were to implement this particular delegate method in a production app you would likely not implement it as basic as we have here. This tutorial is merely just scratching the surface. Instead, you might consider checking how far the user has moved and if it’s within a few meters, you might just choose to keep the map still and let the dot move around the view a little.
You can download the project here and find the full source code below:

ViewController.swift




import UIKit
import MapKit

class ViewController: UIViewController , MKMapViewDelegate {
    
    enum MapType:NSInteger {
        case StandardMap = 0
        case SatelliteMap = 1
        case HybrideMap = 2
    }

    @IBOutlet weak var mapView: MKMapView!
    var loactionManager = CLLocationManager.init()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        loactionManager.requestWhenInUseAuthorization()
        mapView.delegate = self
        mapView.mapType = .standard
        mapView.showsCompass = true
        mapView.showsUserLocation = true
        mapView.showsScale = true
    }

    @IBAction func mapTypeChanged(_ sender: UISegmentedControl) {
        switch sender.selectedSegmentIndex {
        case MapType.StandardMap.rawValue:
            mapView.mapType = .standard
        case MapType.SatelliteMap.rawValue:
            mapView.mapType = .satellite
        case MapType.HybrideMap.rawValue:
            mapView.mapType = .hybrid
        default:
            break
        }
    }
    
    func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
        mapView.setCenter(userLocation.coordinate, animated: true)
    }
}




Output:- 









Comments

Popular posts from this blog

UISearchBar and UISearchBarDelegate

Easy way to learn Swift (IOS)