© Jeffrey Linwood 2020
J. LinwoodBuild Location Apps on iOS with Swifthttps://doi.org/10.1007/978-1-4842-6083-8_8

8. Exploring Google Map Views

Jeffrey Linwood1 
(1)
Austin, TX, USA
 

In the previous chapter, we did all of the setup necessary to work with the Google Maps SDK for iOS. If you have not worked through Chapter 7, start there, as this chapter will pick up where that one left off.

In this chapter, we will explore the capabilities of the Google Maps map view. In particular, we will work with map types, map markers, and shapes.

Changing the type of map tiles

Get started by opening the application you built in Chapter 7. We are going to extend that project, so we do not have to repeat the setup process for Google Maps.

Google Maps comes with five different map types, which are specified in the GMSMapViewType enum:
  • hybrid – Satellite tiles with labels

  • none – No tiles, no labels

  • normal – Default option, street map with labels

  • satellite – The satellite tiles, but with no labels

  • terrain – Useful for outdoor apps, has labels

Setting the map type is straightforward – in the viewDidLoad() method of the ViewController class from Chapter 7, add one line at the bottom, as shown in Listing 8-1.
 override func viewDidLoad() {
  super.viewDidLoad()
  ...
  mapView.mapType = .terrain
}
Listing 8-1

Changing the map type for Google Maps

You can always allow your users to set the map type themselves if you want to let them switch between satellite, terrain, and street map options. The map type can be changed at runtime without a problem.

Displaying map markers

Displaying a map marker on Google Maps is straightforward – simply create a map marker as a GMSMarker object, specify the position and title, and then tell the marker which map it belongs to.

This is a little different than MapKit, where you add a marker to a map directly. Create a new function in your ViewController class named addMarker(), with the code in Listing 8-2.
func addMarker() {
  let marker = GMSMarker()
  marker.title = "Austin"
  marker.snippet = "Texas"
  marker.position = CLLocationCoordinate2D(
    latitude: 30.25, longitude: -97.75)
  marker.map = mapView
}
Listing 8-2

Adding a marker to the map

Add a call to addMarker() in your viewDidLoad() method , and run the application. You should see something similar to this after clicking the map marker to display the info window. The info window in Figure 8-1 will show the title and snippet properties, if they exist.
../images/485790_1_En_8_Chapter/485790_1_En_8_Fig1_HTML.jpg
Figure 8-1

Showing a map marker and info window on the map

Changing the marker icon

By default, markers will show up as a red pin. The marker’s icon and iconView properties control how the marker displays. The icon property takes a UIImage object. The iconView property takes a UIView object. The iconView property takes precedence over the icon property – if you set an iconView, the icon property will be ignored.

The GMSMarker class provides a helper function that creates a UIImage of the marker icon with the color that you specify (as a UIColor). This is helpful if you want to show markers with different colors on your map. To change the marker icon to blue, you would see something like this:
marker.icon = GMSMarker.markerImage(with: .blue)

You can pass any color in as an argument to the markerImage() method .

Responding to marker events

The GMSMapViewDelegate protocol contains many different callback methods you may end up using with your map application.

With respect to markers, the most common functionality you would implement would probably be to do something when a user taps on a marker or on an info window. Typically, if the marker has an info window, you would only do something if the user taps the info window, but it is possible to listen for taps on the marker, do something, and also still display the info window.

The first step would be to set the delegate property of the mapView to the view controller:
mapView.delegate = self
Next, create an extension of your ViewController class that implements the GMSMapViewDelegate protocol:
extension ViewController: GMSMapViewDelegate {
}
To do something when a user taps on an info window, implement the mapView(_ didTapInfoWindowOf marker:) method of the protocol:
func mapView(_ mapView: GMSMapView,
  didTapInfoWindowOf marker: GMSMarker) {
    print("Info: (marker.title ?? "No Title")")
}
You have access to the map view, as well as the marker associated with that info window. If you need to do something when the user taps on an info window, implement the mapView(_didTap marker:) method. Return true if the map view does not need to process the default behavior for the tap (showing an info window) or false if the map view should proceed with the default behavior. The following code snippet returns false, so the map view will run this code and then display the info window:
func mapView(_ mapView: GMSMapView,
  didTap marker: GMSMarker) -> Bool {
    print("Marker: (marker.title ?? "No Title")")
    return false
}

User data and markers

The preceding examples simply print out the marker’s title, if there is one. Typically, you’d want to present a new view controller with details about the place represented by the selected marker. That requires some kind of way to link the selected marker object with an identifier or a data object. The GMSMarker class has an optional userData property that can be used to store anything – strings, numbers, or your own objects. The Google Maps for iOS SDK will simply ignore it – your code is responsible for doing something with it. This makes passing in custom data very easy – if your map markers are showing City objects, for instance, simply set the user data property to the corresponding instance of the City class. You can also pass in an identifier, like an integer or a UUID, and then let your details view controller do the lookup from a data store.

We can try this out by adding user data to our marker in the addMarker() method:
 marker.userData = 1234
If we then modify the mapView(_didTap marker:) method in our extension, we can see the user data printed out:
func mapView(_ mapView: GMSMapView,
  didTap marker: GMSMarker) -> Bool {
    print("Marker: (marker.title ?? "No Title")")
    print(marker.userData ?? "No user data")
    return false
}

Again, you would probably use this to present a new view controller with more details about the marker, but this illustrates how you might retrieve this user data from the marker.

Adding shapes to the map

You may want to add lines, polygons, or circles to your map. For instance, if you had driving directions from one place to another, you might want to show the entire path on the map. You might also want to do a data visualization using circles on the map, for instance, to illustrate magnitude of earthquake shocks or the number of people living in a city or town.

Circles

Generally speaking, all of these work similarly, so let’s start with the least complicated example – a circle. The GMSCircle class is like the GMSMarker class, in that you set its position with a latitude and longitude and then assign a map as a property. It differs from a marker because you have to set a radius that the circle covers on the map. This radius is in meters and corresponds to an actual geographic location. A marker would generally display at the same fixed size, no matter what zoom level you are at in the map. In addition to setting a radius, you also may set the fill color for the inside of the circle, the color used on the stroke outline of the circle, and the width of that outline.

The following code creates a light gray, semi-transparent circle over Austin, Texas, with a 20-kilometer radius and a dark gray outline:
func addCircle() {
  let circle = GMSCircle()
  circle.fillColor = UIColor(white: 0.6, alpha: 0.7)
  circle.strokeColor = .darkGray
  circle.radius = 20 * 1000
  circle.position = CLLocationCoordinate2D(
    latitude: 30.25, longitude: -97.75)
  circle.map = mapView
}
Make a call to addCircle() from your viewDidLoad() method , run your app, and you will see something similar to Figure 8-2 appear in the Xcode simulator.
../images/485790_1_En_8_Chapter/485790_1_En_8_Fig2_HTML.jpg
Figure 8-2

Displaying a circle on the map

Now that we’ve displayed a circle, let’s move on to the next type of overlay, a polyline.

Polylines and paths

If you are showing a route or a path in your application, you can use polylines to display one line that consists of one or more different segments. With the Google Maps SDK for iOS, the way this works is that you construct a path as a GMSMutablePath object with two or more location coordinates, in the order that they will be connected. You then construct a GMSPolyline object with that path. The path represents the data (latitudes and longitudes, as well as the order), and the polyline provides the rendering options for the map – line color and width. There is no fill color for the polyline with Google Maps – only a stroke color and a stroke width.

Start by creating a path:
let path = GMSMutablePath()
You can add CoreLocation coordinates or latitude and longitude pairs to the path. For this example, let’s use latitude and longitude pairs to add several cities in Texas to the path. This path will have two segments:
path.addLatitude(30.25, longitude: -97.75)
path.addLatitude(29.4, longitude: -98.5)
path.addLatitude(29.76, longitude: -95.37)
Now that we have a path with two or more coordinates, we can construct a polyline with the path:
let polyline = GMSPolyline(path: path)
You can set the display options for the polyline by defining a new stroke color and stroke width:
polyline.strokeColor = .red
polyline.strokeWidth = 3
Last, you do need to remember to tell the polyline which map to display on:
polyline.map = mapView
Here are all of these statements combined into one function:
func addPolyline() {
  let path = GMSMutablePath()
  path.addLatitude(30.25, longitude: -97.75)
  path.addLatitude(29.4, longitude: -98.5)
  path.addLatitude(29.76, longitude: -95.37)
  let polyline = GMSPolyline(path: path)
  polyline.strokeColor = .red
  polyline.strokeWidth = 3
  polyline.map = mapView
}
If you were to call that function from your app, it would look similar to Figure 8-3.
../images/485790_1_En_8_Chapter/485790_1_En_8_Fig3_HTML.jpg
Figure 8-3

Displaying a polyline from a path of latitude and longitude pairs

Typically, you would probably construct the path in a loop, reading from a database, the JSON response of an API call, or some other data structure. If you are working with the Google Directions API, you can create the path from the encoded path that a valid route returns. We’ll build a driving directions application that uses the Google Directions API and Google Maps in the next chapter.

Polygons

Polygons are similar to polylines, in that you construct them with a path of coordinates. The difference is that polygons will draw an additional line between the last coordinate and the first coordinate, completing the outline of the shape. In addition, similar to circles, you can set a fill color on a polygon. Here is an example of a polygon drawn using the same coordinates as the polyline earlier:
func addPolygon() {
  let path = GMSMutablePath()
  path.addLatitude(30.25, longitude: -97.75)
  path.addLatitude(29.4, longitude: -98.5)
  path.addLatitude(29.76, longitude: -95.37)
  let polygon = GMSPolygon(path: path)
  polygon.strokeColor = .black
  polygon.strokeWidth = 2
  polygon.fillColor = UIColor(
      red: 1, green: 0, blue: 0, alpha: 0.3)
  polygon.map = mapView
}

The only differences between the preceding code and the polyline code are using the GMSPolygon class instead of GMSPolyline and being able to specify a fill color.

Call the addPolygon() method at the end of your viewDidLoad() method , and you should see a triangle appear, like the one in Figure 8-4.
../images/485790_1_En_8_Chapter/485790_1_En_8_Fig4_HTML.jpg
Figure 8-4

Displaying a polygon on the map

Now that you’ve seen how to draw shapes on the map, it’s time to discuss how to remove shapes after they are no longer needed.

Removing markers and shapes

Each marker or shape has a map property that points to the map view they display on. If you would like to clear a marker or shape from the map, simply set the map property to nil:
mapMarker.map = nil
This is useful for removing individual markers or shapes or different groups of markers or shapes. If you’re processing search results and then displaying them on the map, you may want to clear all previous results off of the map before displaying the new results. To clear all markers and shapes off of the map and only display the map tiles, use the clear() method on the map view:
mapView.clear()

You can now create any new markers or shapes that you need.

Conclusion

In this chapter, we discussed how to change the map tiles used in your map view, using the mapType property of the map view. We also added markers to our map and learned how to change the marker icon. When a user taps on a marker or info window, we also learned how to handle those events and how to pass custom data on a marker that we could use. Last, we looked at adding circles, polylines, and polygons and then how to remove markers and shapes.

In the next chapter, we build on our discussion of polylines and paths to provide driving directions from Google Directions API on a map view.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.85.76