How to make a custom info window for your marker with Google Maps SDK for iOS

This is the third part of our Google maps SDK series. In the first tutorial, you Learned how to use the Google maps SDK to deliver search suggestions, autocompleted to the user and based on what he types in. The second tutorial is also as much important as the first, where it shows you how to implement the powerful nearby API in your app to overcome Apple limits and make your app special.

In this quick tutorial, you will customise the marker icons as well as the info window view on your map.

Without further ado, download the starter project and let’s get this done!

Make sure to open the file named CustomInfoWindow.xcworkspace since cocoapods is now integrated with the project and the Google Maps SDK for iOS is installed.

If you take a look at the Project navigator view, I added two files called CustomInfoWindow.xib and CustomInfoWindow.swift files. This two files will manage the custom info window layout and outlets. Also, few assets for the custom markers icons and info window are already added to the Assets.xcassets catalog.

Note 1: Before you continue, make sure to generate a Google API Key. If you don’t have one already, head over here to make your own (you may need to follow only steps 4 and 5 of the guide).

Note 2: Also, make sure that the bundle identifier of the app (in this case: com.medigarage.CustomInfoWindow) is registered in order to be able to use the API Key. Head over here to register the bundle identifier with the API Key if you didn’t already.

Good, now you are all set to go!

First thing to do is to provide the API Key in the app, select the AppDelegate.swift file from the Project navigator view and make the following changes:

Change the argument in the provideAPIKey method with the API Key you just generated in the Google APIs Console.

Next, select the ViewController.swift file from the Project navigator view and import the Google Maps SDK at the top of the file:

We will need some properties for the maps and its data, copy the following declarations just after the class name:

Apart the map property which works as a reference for the map view, all the properties declared above are arrays to use to provide the markers with coordinates and the info window views with informations.

Let’s initialise the arrays with some data, place the following code inside the viewDidLoad method:

The app you are about to build will show three historical monuments, in Paris, London and Rome. The arrays above are initialised with the coordinates, architects names and years of construction completion.

Next, you will load the map and make it take the whole screen frame. Always in the viewDidLoad method, implement the following code:

Setting the current controller as the delegate of the map view protocol requires making the class itself to adopt to the GMSMapViewDelegate protocol. To do so, just change the class declaration as below:

Cool, time to add some markers to the map πŸ™‚

Implement the code below in the viewDidLoad method:

The code above will loop 3 times, the number of assets we got. It will use the lon/lat coordinates from the arrays to place the markers on the map. It will also change the default red icon with a custom image and modify the anchor of the info window slightly so it appears just above the icon. Finally the accessibilityLabel is set with the index of the icon in order to tag it with a reference for later use.

To finish up, let’s implement the mapView:markerInfoWindow: delegate method to allow the app to use the custom info window you just have in the CustomInfoWindow.xib file.

Place the following code before the class closing bracket:

The protocol method above will be called each time you select a marker, and it’s intended to return a custom view for each marker, so this is the right place where to customise the info window πŸ™‚

First, the code will get a reference for the selected marker, this become easy since you use the accessibilityLabel property you set it earlier. second, you load the custom info window file and cast it to the CustomInfoWindow class in order to be able to use the properties hooked up in the view.

That’s it! Run the app and enjoy your work πŸ™‚

As usual, here is the final project, feel free to leave your comment below. I am always open to your suggestions!

About Malek

Malek is a passionate iOS Engineer and Founder of Medigarage Studios, a small mobile games startup. I started my iOS adventures in 2011 and since then I fell in love with it. You can hire me for your project, get in touch by Email to discuss further details. Also, feel free to reach out on Twitter and Google+.

  • Pingback: Android & iOS Application Development | Just another My blog Sites site()

  • Tristan A

    Hey Malek. Again, thanks for the tutorial. Very helpful as always.

    Quick question: do you know the best way to add a user editable UITextView to the custom info window?

    I tried by adding a text view object to the xib and creating a UITextView outlet in the CustomInfoWindow class. The text view box shows up in the simulator but isn’t editable. Any thoughts?

    Thanks man

  • Malek_T

    Hi Tristan πŸ™‚
    I would suggest putting the UITextView outside the map, ’cause placing it on the map view will drop the user experience. For example:
    1- When click on the marker view, an alert view shows up with a UITextField inside
    2- User enters his input and click OK
    3- When alert’s OK is clicked, the marker content is updated accordingly.

    Hope this helps.

  • Guy Kahlon

    Hey Malek.
    Thanks for very helpful tutorial.

    A question: How can I load an asynchronies Image to my InfoWindow?
    When I load the image synchronize everything works fine but when I load the image asynchronies it doesn’t work for me.

    func mapView(mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {

    guard let piece = marker.userData as? Piece else {
    return nil

    let view = PieceMapAnnotationView(frame: CGRect(x: 0, y: 0, width: 320, height: 120))

    view.descriptionLabel.text =
    piece.loadPhoto { (photo) -> () in

    NSOperationQueue.mainQueue().addOperationWithBlock {
    view.imageView.image = photo

    return view