Skip to content

iOS: Static MapView with PROVIDER_GOOGLE silently blocks gestures on a second interactive MapView #5899

@2z2z2z

Description

@2z2z2z

Environment

  • react-native-maps: 1.27.2
  • React Native: 0.83
  • Expo SDK: 55 (New Architecture / Fabric)
  • iOS: 17+ (tested on simulator iOS 26 and real device via TestFlight)
  • Provider: PROVIDER_GOOGLE on both instances

Summary

When two MapView instances with PROVIDER_GOOGLE coexist in the same view hierarchy on iOS -- a non-interactive "preview" one (scrollEnabled={false}, zoomEnabled={false}, rotateEnabled={false}, pitchEnabled={false}, pointerEvents="none") and an interactive fullscreen one inside a Modal -- all gestures (pan / zoom / tap) on the interactive one are silently blocked. onMapReady fires, tiles render, onRegionChangeComplete fires once for the initial region, but no subsequent onRegionChange, onPanDrag, or onPress events are emitted. This persists across Modal re-opens until the app is restarted.

Reproduction

Minimal repro is straightforward:

  1. Render a screen that contains a <MapView provider={PROVIDER_GOOGLE} scrollEnabled={false} zoomEnabled={false} ... /> as a static preview.
  2. On tap, open a Modal containing an interactive <MapView provider={PROVIDER_GOOGLE} />.
  3. Try to pan/zoom/tap the interactive map. Nothing happens.
  4. Remove the static preview from the screen entirely -> the interactive map works normally.

Root cause

Confirmed by Xcode View Hierarchy Debugger: both GMSMapView instances register native UIGestureRecognizer objects. Disabling gestures via scrollEnabled={false} etc. does not remove the recognizers, it only flips their enabled state. They remain in the hit-testing chain, where they interfere with the recognizers of the second GMSMapView.

This is consistent with Google's own statement: "Maps SDK for iOS has not been optimized for multiple windows/instances and may result in undefined behavior." -- https://developers.google.com/maps/documentation/ios-sdk/start

Workaround (what actually works)

Use Apple Maps (default provider, provider={undefined}) for the non-interactive preview on iOS, with cacheEnabled={true} so it renders as a cached image. Keep PROVIDER_GOOGLE for the interactive fullscreen map. This works because Apple Maps (MKMapView) and Google Maps (GMSMapView) use entirely separate gesture stacks.

// iOS: Apple Maps + cacheEnabled (static image, no gesture recognizers)
// Android: Google Maps + liteMode (bitmap)
<MapView
  provider={Platform.OS === 'ios' ? undefined : PROVIDER_GOOGLE}
  scrollEnabled={false}
  zoomEnabled={false}
  rotateEnabled={false}
  pitchEnabled={false}
  liteMode={Platform.OS === 'android'}
  cacheEnabled={Platform.OS === 'ios'}
  pointerEvents="none"
/>

What I think the library could do

Three possible directions, in rough order of preference:

  1. Documentation warning. Add an explicit note in the README / PROVIDER_GOOGLE docs that two PROVIDER_GOOGLE instances in the same view hierarchy on iOS can break gestures, and recommend Apple Maps + cacheEnabled (or liteMode on Android) for static previews. This is the cheapest fix and immediately saves people days of debugging.

  2. iOS equivalent of liteMode. Implement iOS liteMode via MKMapSnapshotter (Apple Maps) or via GMSMapView.takeSnapshot + replace-with-image lifecycle (Google Maps). Discussed in Feature: liteMode support on iOS via MKMapSnapshotter? #2497 and liteMode IOS #1411 but never shipped. A concrete modern use case: mobile apps that show a small map preview on a list/detail screen and open a fullscreen interactive map on tap. Without liteMode on iOS, this pattern silently breaks.

  3. Explicit API to detach gesture recognizers. A prop like gestureRecognizersEnabled={false} that calls -[UIView removeGestureRecognizer:] on all recognizers of the underlying GMSMapView, not just flips their enabled flag. This would be more targeted than liteMode for users who want a live (not cached) static map without gesture interference.

Additional notes

  • This is not a regression in react-native-maps -- it's a Google SDK limitation. But the library is where developers hit it, and documenting the workaround here would be high-leverage.
  • The symptom is particularly confusing because the broken map still renders tiles and fires onMapReady. Developers naturally look for overlays, pointerEvents, Modal issues, etc. before suspecting a second MapView instance elsewhere on the screen.
  • Issue Feature: liteMode support on iOS via MKMapSnapshotter? #2497 ("Feature: liteMode support on iOS via MKMapSnapshotter") was closed 7+ years ago without resolution. Filing this fresh with a concrete repro and confirmed workaround seems more useful than commenting on a stale thread.

Happy to provide a minimal repro repo if helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions