Android Custom Info Window for GoogleMaps Marker responsive to touch events

When the user clicks on a marker, an info window is expected. Here, we demonstrate how to add a custom info window (a subclass of UIView) which responds to user touch events.

First, we must create the CustomCalloutView, a container which can hold any content. Regardless of the contents, we must add a tap gesture recognizer so that this callout can respond to touch events.

```swift
class CustomInfoWindowView: UIView {
    convenience init() {
        let tap = UITapGestureRecognizer(target: self, action: #selector(onTap))
        tap.numberOfTapsRequired = 1
        self.addGestureRecognizer(tap)
    }

    @objc func onTap() {}
}
```

We must also create a structure that links a marker with its associated custom info window.
When a custom info window is showing, we should maintain a reference to this structure so that future updates to its position can be performed. 

```swift
struct SelectedInfoWindow {
    
    var marker: GMSMarker? = nil
    var infoWindow: UIView? = nil
    
    init(infoWindow: UIView, marker: GMSMarker) {
        self.infoWindow = infoWindow
        self.marker = marker
    }
}
```

When the user taps a marker, we add the CustomCalloutView as a subview of the mapView, making sure to generate the reference structure above after removing past info windows.

We also immediately set the position of the info window by calling self.updateInfoWindowPosition() as described below. This function must return "true" to ensure that the default info window does not appear.

```swift
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
	
    self.removeInfoWindow()
    self.mapView?.subviews.filter{ $0 is CustomInfoWindowView }.forEach{
        $0.removeFromSuperview()
    }

    let infoWindow = CustomInfoWindowView()
    self.selectedInfoWindow = SelectedInfoWindow(infoWindow: infoWindow, marker: marker)
    self.mapView?.addSubview(infoWindow)
    self.updateInfoWindowPosition()

    return true
}
```

Here, an info window is removed and the reference structure deleted.

```swift
func removeInfoWindow() {
    guard
        let window = self.selectedInfoWindow?.infoWindow,
        let marker = self.selectedInfoWindow?.marker
        else {
        return
    }
    
    infoWindow.removeFromSuperview()
    self.selectedInfoWindow = nil
}
```

The central position of the info window is set relative to the center point of its associated marker, using mapView?.projection.point(for:). It is recommended to offset these centers so that the marker is not covered by the info window; this offset is therefore calculated from the size of the marker icon and custom infoWindow.

We should also call this function if the CustomCalloutView layout changes (i.e. override layoutSubViews to catch this event), because the infoWindow offset may have changed.

```swift
public func updateInfoWindowPosition() {
           
    guard
        let infoWindow = self.selectedInfoWindow?.infoWindow,
        let marker = self.selectedInfoWindow?.marker,
        let center = self.mapView?.projection.point(for: marker.position)
        else { return }
    
    window.center = center

    let markerHeight = marker.icon?.size.height ?? 0.0
    let infoWindowHeight = infoWindow.frame.height
    var offset: CGFloat = -0.5 * infoWindowHeight - markerHeight
    
    infoWindow.frame = window.frame.offsetBy(dx: 0.0, dy: offset)
}
```

When the map position changes, we must continue to update the infoWindow position.

```swift
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
    self.updateInfoWindowPosition()
}
```

Finally, when the user deselects the marker by pressing the map at a location other than a marker, we must call the method to remove the info window.

```swift
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
    self.removeInfoWindow()
}
```