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.