How to Implement Custom and Dynamic Map Marker Info Windows for Google Maps iOS
⚠️ This article is now over a year old and such some code might be outdated.
If you plan on implementing a map view in your iOS app, chances are you’re leaning towards using Google Maps, as opposed to Apple Maps — and I don’t blame you. Mapping apps have two common uses: providing directions, and indicating specific locations throughout the map. If your app will include the latter, map markers are the go-to tool to facilitate it. Info Windows are the views presented when a user taps on a marker — and because this is Swift, there’s a lot of potential functionality and information that can be presented in these views. While the SDK guide provided by Google does go into great detail on every individual map component supported, they don’t make it obvious as to how to piece it all together. Now, as I learned while working on my first map-centered app, not only are there no obvious tutorials or guides provided by Google themselves, there also aren’t any complete and polished guides available anywhere online.
Assumptions
Google Maps should be implemented and working in your app already. If you need help implementing Google Maps itself, there are plenty of easy to follow guides available throughout the web, and by Google themselves here: https://developers.google.com/maps/documentation/ios-sdk/start
If you plan on implementing dynamic info windows, your App should also be backed by a database — in my case, I’ll be using Firebase. The database entries will store the coordinates of all your markers, as well as some data related to it.
In this guide, I’ll be going through creating a custom XIB file for your markers, displaying marker data in its UILabels, and handling button press events done on the marker. This will be done using Swift 3.
1. Creating the Custom Marker View
The first step is creating a new view file, which I named MapMarkerWindowView.xib
. The XIB file view should be resized to your preferred info window size, and should hold all of your desired UI elements, such as UILabels, UIButtons, UISwitches, and anything else you might want to include. Don’t forget to lock all these elements into place using constraints. In this example, I have three labels, and one button.
2. Making the Associated Class File
Next, create a new Swift file that will contain the class to control your view file, mine is named MapMarkerWindow.swift
. This class should extend UIView, and have IBOutlet references to your UI elements. If you include elements in your view which require event handlers, such as UIButtons, reference these in your class with IBActions. In order to let your MapViewController class know these events have been triggered, you’ll need to create a delegate protocol along with event handling methods in your MapMarkerWindow.swift
, and implement it in your MapViewController class. Additionally, create a public class method to instantiate the XIB file you’ve previously created. This will be required later when assigning the custom info window to the markers.
3. Loading the Marker Data from your Database
Moving to the class that manages your Google Maps view, you’ll need to gather a dictionary of data relating to each marker you want to place on your map — this must include coordinates for the marker location. As previously mentioned, I’m storing my marker data on Firebase. Retrieve your marker data whichever way you need to, and make sure the latitude
and longitude
values are readily available. In my case, I will load all the data for each individual marker and store in a dictionary I call spot
. Once you have the data, you’re ready to create the map marker — we’ll do this on a the main thread to offload the computations from the UI thread. At this point, you can use an image as a custom marker image and color it as you desire. Assign the position
property of the marker to the coordinate values you previously loaded, and don’t forget to assign the userData
property of the marker to the spot
dictionary containing all the marker data. This step is important as any data you want to display on the info window will need to part of this userData
dictionary associated to the marker. You should call this method from viewDidLoad
.
4. Implement the GMSMapViewDelegate methods
Have your MapViewController extends from the GMSMapViewDelegate
protocol. This will require you to implement a few methods, but first create two class properties:
private var infoWindow = MapMarkerWindow()fileprivate var locationMarker : GMSMarker? = GMSMarker()
Next, create a function to return an instance of the custom view class you created
In viewDidLoad
, be sure to assign self.infoWindow = loadNiB()
.
Now, we’ll implement some GMSMapViewDelegate
methods. The primary method, which will allow you to get all your map data displayed on the info window, is the didTap marker
method. Using the marker
parameter passed in, we can access all of that markers data through marker.userData
. Set your self.locationMarker
class property to be this marker — this is so that we can later handle moving the info window appropriately if the map gets moved. We also want to reset the self.infoWindow
class property to be loadNiB()
again. At this point we can do any UI element configuration to the info window, such as adding a corner radius, or lowering the opacity. We also set the text of our info window’s label(s) here using the data provided in marker.userData
. We also want to place the center point of the info window’s view to equal the center point of the marker here. A negative Y value offset will be required to place the info window above the marker.
The other two delegate methods from the GMSMapViewDelegate
protocol we will implement are didChange position
and didTapAt coordinate
. If the map view was moved while an info window is open, we want the info window to properly reposition itself to stay on top of the marker it was sourced from. Additionally, if a user taps elsewhere on the map, we will dismiss the info window from being presented.
At this point, if we run our app, the info window should be presented with its appropriate data.
5. Handle event triggers from the info window
If we want to execute a bit of code from the MapViewController when a button on the info window is tapped, we simply implement the MapMarkerDelegate
protocol in that class. This will force us to call the didTapInfoWindow
method we defined in that protocol. From here, we have full access to that markers data. This can be useful if we want to push a new view controller that requires the spots information.
That’s it! This should piece together all the working parts required to get a custom info window for your maps markers.