The default map view for iOS, since 2012, is Apple Maps. However, you are still able to use Google Maps in your project thanks to the Google Maps SDK.
In order to use the Google Maps SDK in your projects, you must obtain an API key using Google Platform Console. You can create an API key for your project by clicking here.
Recently, Google changed their Cloud Platform pricing plans so that you must enable billing to receive an API key to use the Google Maps SDK in your project. View more details about pricing here before signing up.
The fastest way to import any of the Google SDKs into your project is through a Cocoapod. Adding a Cocoapod to your iOS app is fast and easy.
In this app we’re going to be using the Google Maps and Google Places SDK Cocopods. To add those libraries to our project copy and paste these pods into your podfile.
pod 'GoogleMaps' pod 'GooglePlaces'
Save and exit your podfile. Type pod install
in your terminal to install the pods we added.
Now, you need to initialize the Google Maps and Google Places SDK with your API key you created earlier. Open AppDelegate.swift file and provide the SDKs with an API key inside the application didFinishLaunchingWithOptions
method like this:
import GoogleMaps import GooglePlaces ... func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GMSServices.provideAPIKey("googleApiKey") GMSPlacesClient.provideAPIKey("googleApiKey") return true }
Go to the Map.storyboard file. Click on the main view inside the scene or create a custom UIView
with the size you need, and go to the identity inspector and give it a custom class of GMSMapView
. Now we need to connect our map view to the MapViewController using an outlet.
There’s one last step to do before we have a working map view in our project. We need to set the map view’s delegate to be the MapViewController class.
Create an extension on MapViewController and conform to GMSMapViewDelegate
protocol like this:
extension MapViewController: GMSMapViewDelegate { }
Lastly, in the viewDidLoad
method, assign the map view delegate to be the MapViewController class.
Your MapViewController should look something like this now:
import UIKit import GoogleMaps class MapViewController: UIViewController { @IBOutlet var mapView: GMSMapView! override func viewDidLoad() { super.viewDidLoad() mapView.delegate = self } } extension MapViewController: GMSMapViewDelegate {}
We can specify the initial location that will be displayed in the map. The following coordinates center the map in Paris. Besides the coordinates, we also need to specify the initial zoom level of the map.
let camera: GMSCameraPosition = GMSCameraPosition.cameraWithLatitude(48.857165, longitude: 2.354613, zoom: 8.0) mapView.camera = camera
Current Location
Getting the users current location requires that we use CoreLocation. With that, we get the current user location and use that value to update the camera on the Google Map.
In MapViewController, import CoreLocation, just below the import GoogleMaps line.
import UIKit import GoogleMaps import CoreLocation
We also need to create a CLLocationManager
and set the delegate as well as request authorisation and start getting the locations. We do that by adding the following:
let locationManager = CLLocationManager() override func viewDidLoad() { super.viewDidLoad() locationManager.requestWhenInUseAuthorization() locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest }
Next, adopt the CLLocationManagerDelegate
protocol where the class is declared:
extension MapViewController: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { guard status == .authorizedWhenInUse else { return } locationManager.startUpdatingLocation() mapView.isMyLocationEnabled = true mapView.settings.myLocationButton = true } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let location = locations.first else { return } let loc = location.coordinate let marker = GMSMarker(position: loc) marker.title = "You are here" marker.map = mapView mapView.camera = GMSCameraPosition(target: loc, zoom: 15, bearing: 0, viewingAngle: 0) locationManager.stopUpdatingLocation() } }
We need to add an entry to Info.plist. Select the file, then right click on the first key (Information Property List) and select "add row".
You need to edit the key name for this and set it at NSLocationWhenInUseUsageDescription
(Privacy – Location When In Use Usage Description) and "Testing Google Maps Integration" as value.
Adding a Marker
Adding a marker on the map is a really easy task, as doing so it only takes two lines of code. However, there are various properties that can be configured so a marker can be customized according to your needs or preferences. In the Google Maps SDK a marker is an object of the GMSMarker
class. When initializing such an object, you must specify the longitude and latitude expressed as a CLLocationCoordinate2D
type.
let coordinate = CLLocationCoordinate2D(latitude: 48.857165, longitude: 2.354613) var locationMarker = GMSMarker(position: coordinate) locationMarker.map = viewMap locationMarker.title = "Paris" locationMarker.snippet = "The best place on earth." locationMarker.appearAnimation = .pop locationMarker.icon = GMSMarker.markerImageWithColor(UIColor.blueColor()) locationMarker.opacity = 0.75
InfoWindow
In Google Maps, an InfoWindow
is a popup window with extra information about a given location. It is displayed when the user taps on the marker we added above. You can attach your own UIView with whatever components you need.
override func viewDidLoad() { super.viewDidLoad() let camera = GMSCameraPosition.camera(withLatitude: 51.5287352, longitude: -0.3817818, zoom: 8) mapView.camera = camera let marker = GMSMarker() marker.position = CLLocationCoordinate2D(latitude: 51.5287352, longitude: -0.3817818) marker.title = "My marker" marker.map = self.mapView self.mapView.delegate = self }
Next, we want to show an InfoWindow
when the user clicks on the marker. To achieve this, we will use a xib file. In XCode create a new View file and name it CustomInfoWindow
.
In the xib file, click on the Attributes inspector and change the view’s size to Freeform. Then change the Size of the view using the Size inspector and set width to 300 and height to 200.
To add some functionality we add a UILabel
and a UIButton
by dragging them.
Now, we need to create a class for our custom InfoWindow
. Create a new CocoaTouch file and name it CustomInfoWindow
. This class should be a subclass of UIView.
In order to connect the view to our file we have to set it as a class in the Identity Inspector of the view.
Now we want to be able to show our InfoWindow
. To do this, we firstly should be able to instantiate the xib file. In you CustomInfoWindow
file add following code.
override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } func loadView() -> CustomInfoWindow{ let customInfoWindow = Bundle.main.loadNibNamed("CustomInfoWindow", owner: self, options: nil)?[0] as! CustomInfoWindow return customInfoWindow }
After we have done this, we can now proceed in the ViewController
. We declare two attributes: tappedMarker
and currentInfoWindow
. We need to keep track of the tappedMarker
and have a reference to the currentInfoWindow
in order to change it or customize it.
var tappedMarker : GMSMarker? var customInfoWindow : CustomInfoWindow?
Now we have to instantiate them in viewDidLoad
.
self.tappedMarker = GMSMarker() self.customInfoWindow = CustomInfoWindow().loadView()
As next, we have to implement two methods of the GMSMapDelegate
; namely mapView(didTap, marker)
and mapView(markerInfoWindow, marker)
in order to catch the tap events on markers and display an info window. Now you can add this to your ViewController file.
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { return false } func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? { return self.customInfoWindow }
Geocoding
Now that you have the user’s location, it would be nice if you could show the street address of that location. Google Maps has an object that does exactly that: GMSGeocoder
. This takes a simple coordinate and returns a readable street address.
func reverseGeocodeCoordinate(_ coordinate: CLLocationCoordinate2D) { let geocoder = GMSGeocoder() geocoder.reverseGeocodeCoordinate(coordinate) { response, error in guard let address = response?.firstResult(), let lines = address.lines else { return } self.addressLabel.text = lines.joined(separator: "\n") UIView.animate(withDuration: 0.25) { self.view.layoutIfNeeded() } } }
You’ll want to call this method every time the user changes their position on the map. To do so you’ll use GMSMapViewDelegate
.
extension MapViewController: GMSMapViewDelegate { func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) { reverseGeocodeCoordinate(position.target) } }
Links