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

Open Beginner friendly
#5,899 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
66/100
Issue type
Documentation
Clarity
Mostly clear
Activity status
Quiet
Tech stack
ios, react-native

Research direction

Start with the README and the existing PROVIDER_GOOGLE documentation. Add a warning about multiple Google MapView instances on iOS interfering with gestures, and document the Apple Maps plus cacheEnabled workaround for static previews. Check the rendered documentation to confirm the warning and recommendation are clear.

Written by the indexing model from the issue text.

Description

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 #2497 and #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 #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.

Dominant language
TypeScript
Stars
16k
Forks
5k
Avg merge
9d 9h
Merged PRs (30d)
3

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from react-native-maps/react-native-maps

All issues in react-native-maps/react-native-maps

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.