Skip to content

fix(android): map pans after long-press instead of staying fixed like on iOS#4196

Closed
mariusud wants to merge 32 commits intornmapbox:mainfrom
mariusud:mariusud/fix-drag-android
Closed

fix(android): map pans after long-press instead of staying fixed like on iOS#4196
mariusud wants to merge 32 commits intornmapbox:mainfrom
mariusud:mariusud/fix-drag-android

Conversation

@mariusud
Copy link
Copy Markdown
Contributor

@mariusud mariusud commented Apr 22, 2026

Description

On iOS, long-pressing an item on the map and dragging keeps the map fixed. On android the gesture is allowed through and the map pans instead.

As discussed in #3981.

This PR brings android to feature parity with iOS by consuming the move gesture after a long-press
To test:

  • Long-press a feature on the map
  • Without lifting your finger, drag — the map should stay fixed and the feature should follow your finger
  • This already works on iOS - android should now behave the same way

@mariusud mariusud had a problem deploying to CI with Mapbox Tokens April 22, 2026 12:14 — with GitHub Actions Error
@mariusud mariusud had a problem deploying to CI with Mapbox Tokens April 22, 2026 12:14 — with GitHub Actions Error
@mariusud mariusud had a problem deploying to CI with Mapbox Tokens April 22, 2026 12:14 — with GitHub Actions Error
mfazekas and others added 26 commits April 22, 2026 14:27
* chore(ios): remove old architecture code

Remove React Native old architecture (Paper/Bridge) code from iOS,
keeping only new architecture (Fabric/TurboModules) code.

Changes:
- Delete all *ViewManager.m and *ViewManager.swift files (old arch view managers)
- Remove #ifdef RCT_NEW_ARCH_ENABLED guards from ComponentView files
- Simplify Module.h files to only conform to TurboModule protocols
- Remove old arch conditionals from Module.mm files
- Update RNMBXViewResolver to remove old arch code paths
- Remove unused RCTViewManager imports from Bridge/RNMBX.h and Swift.pre.h
- Remove guards from RNMBXFabricPropConvert.mm and RNMBXFollyConvert.h

This is a follow-up to the Android old architecture removal (rnmapbox#4128)
and continues the 10.3.0 cleanup work (rnmapbox#4118).

* fix(ios): add __cplusplus guard to ComponentView.h files

The umbrella header is compiled as Objective-C (not Objective-C++),
which causes errors when ComponentView.h files import Fabric headers
that transitively include C++ code like <atomic>.

Use #ifdef __cplusplus guard instead of RCT_NEW_ARCH_ENABLED to:
- Allow content when compiled as Objective-C++ (.mm files)
- Hide content when compiled as Objective-C (umbrella header)

* fix(ios): add module wrapper methods to RNMBXMapView

Move 16 module methods from static RNMBXMapViewManager methods to instance methods on RNMBXMapView, fixing build errors after old architecture removal.

* fix(ios): restructure Module.h files for Swift compilation

Move Foundation/UIKit imports outside __cplusplus guard and provide
plain NSObject interface for non-C++ contexts (Swift).

* fix(ios): add module wrapper methods to RNMBXViewport and RNMBXShapeSource

Call methods directly on view instances instead of through Manager classes.

---------

Co-authored-by: Claude <noreply@anthropic.com>
* chore: upgade example to rn 0.82.0

* fix main application

* update yarn.lock after rebase
…#4152)

The CI build was failing because the xcodebuild destination specified
OS=18.6 which may not have simulators pre-created on all macos-15
runner instances. Use generic/platform=iOS Simulator for the build
step, and drop the pinned os version from the detox device config
so it picks whatever is available.
Update simulator device to iPhone 16 and use generic iOS Simulator
build destination for local builds (consistent with CI).
* feat: add HillshadeLayer component

Add full HillshadeLayer support with iOS (Swift + Fabric) and Android
native implementations. Uses raster-dem source for terrain visualization.

* add HillshadeLayer example (Grand Canyon)
* chore: upgrade detox to 20.47.0

Update simulator device to iPhone 16 and use generic iOS Simulator
build destination for local builds (consistent with CI).

* fix(example): fix detox screenshot tests and suppress LogBox warning

- Move LogBox.ignoreLogs to setup.js (loaded before mapbox imports)
- Add whileElement scroll for off-screen example groups
- Add per-example disableSync metadata for tests with JS timers
- Blacklist mapbox tile URLs to prevent Detox sync timeouts
- Fix Ornaments example missing default camera position
- Add Detox e2e docs to CLAUDE.md
v10 and old architecture are no longer supported. Update all docs,
issue templates, and CI to reflect v11-only and New Architecture.
* fix(android): fix onMapSteady event name causing crash

Event key was "topOnMapSteady" which doesn't match the codegen-registered
name. Use "onMapSteady" to match the JS prop directly.

Fixes rnmapbox#4105

* fix(android): fix event payload and timer bugs in CameraGestureObserver

- Override toJSON() in MapSteadyEvent to return flat payload (not wrapped
  in type/payload structure) so JS receives fields on nativeEvent directly
- Fix timer leak in scheduleQuietCheck — cancel previous before scheduling
- Fix scheduleTimeout to not reset on every markActivity, enabling periodic
  heartbeat timeouts during long gestures
- Remove emittedForCurrentActivity flag (no longer needed with timer fixes)
…apbox#4154)

* feat(snapshotter): add withLogo support and Android bounds parity

* Update android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXSnapshotModule.kt

Co-authored-by: Miklós Fazekas <mfazekas@szemafor.com>

---------

Co-authored-by: Miklós Fazekas <mfazekas@szemafor.com>
…box#4132)

* use puckBearingEnabled instead of iosShowsUserHeadingIndicator

* stronger typing
Co-authored-by: Fl0h0 <25343805+Fl0h0@users.noreply.github.com>
…pbox#4165)

* fix(point annotation): selected property should select/deselect a point annotation

Co-Authored-By: Miklós Fazekas <mfazekas@szemafor.com>

* fix(android): use non-nullable Dynamic and log null selected values

* fix(ios): fix callout not rendering on PointAnnotation selection

Callout children were mounted into the ComponentView instead of the
contentView (RNMBXCallout), producing a transparent snapshot. Override
mountChildComponentView in RNMBXCalloutComponentView to insert children
into the contentView. Also set iconAllowOverlap on the callout annotation
manager so callouts are not hidden by symbol collision.

* fix(android): add setReactSelected and mIsSelected tracking to RNMBXPointAnnotation
* fix: remove MapboxV10 constant and dead code branches

MapboxV10 was always hardcoded to true on both platforms. Remove the
constant and all conditional branches that checked it, simplifying the
codebase. Closes rnmapbox#4131.

* keep MapboxV10 constant as deprecated

The constant actually means "v10 or later" and is always true. Keep it
for backwards compatibility but mark as deprecated.
…ndles null style props (rnmapbox#4174)

* fix(android): RNMBXAtmosphere removes atmosphere (not terrain) and handles null style props

Two copy-paste bugs from RNMBXTerrain:
- removeFromMap called removeTerrain() instead of removeAtmosphere(), so
  unmounting <Atmosphere> would silently remove the terrain extrusion instead
- addStyles passed mReactStyle!! (force-unwrap) to RNMBXStyle instead of the
  nullable mReactStyle, crashing on new-arch when no style props are provided
  (setReactStyle is never called, so mReactStyle stays null)

Also clears mAtmosphere in removeFromMap, matching the expected post-removal state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* example: add atmosphere toggle + no-props mode to TerrainSkyAtmosphere

Adds two buttons to reproduce the bugs fixed in RNMBXAtmosphere:
- Remove/Add Atmosphere: verifies that removing atmosphere does not
  accidentally remove the terrain extrusion (removeTerrain bug)
- Atmosphere: no props / styled: switches to <Atmosphere /> with no style
  prop, which crashed on Android new-arch before the mReactStyle!! fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Revert "example: add atmosphere toggle + no-props mode to TerrainSkyAtmosphere"

This reverts commit f5c5180.

* fix(android): guard against null Dynamic in reactStyle managers

On new-arch, setReactStyle is called with a null Dynamic when no style
prop is provided. Calling asMap() on a null Dynamic crashes. Add an
isNull check before asMap() in RNMBXAtmosphereManager, RNMBXTerrainManager
and RNMBXLightManager — the only three managers with a reactStyle prop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(android): add Dynamic.asMapOrNull() extension, use in style managers

Matches the existing asStringOrNull/asBooleanOrNull/asDoubleOrNull pattern
in Dynamic.kt rather than inlining the isNull check at each call site.

* simplify asMapOrNull: delegate to asMap() which already returns ReadableMap?

* fix asMapOrNull: check isNull before calling asMap() which crashes on null

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
g4rb4g3 and others added 5 commits April 22, 2026 14:27
* chore: example for testing screen coordinates

* fix(android): screen coordinate conversion on android

* fix(ios): avoid loss of precision Double -> Float -> CGFloat

* fix: improve docs

* chore: remove pixel conversion comments

* fix: pixel conversion comments
…mapbox#4176)

* fix(android): MarkerView touch/press works correctly on new architecture (Fabric)

On Android with the new React Native architecture, Mapbox positions view
annotations via setTranslationX/Y — a render-layer transform that is invisible
to Fabric's shadow tree. This caused UIManager.measure to return (0,0) for
every MarkerView, so Pressable/TouchableOpacity hit-testing failed, onPress
was never fired, and interactive children (sliders, switches, buttons) didn't
respond to touches.

Fix:
- Override setTranslationX/Y in RNMBXMarkerViewContent to intercept Mapbox's
  position updates and fire a topAnnotationPosition event to JS.
- Deduplicate events to prevent a feedback loop when Fabric re-applies the
  same transform prop back to setTranslationX/Y.
- Override dispatchTouchEvent to call requestDisallowInterceptTouchEvent(true),
  preventing the parent MapView's pan/zoom gesture from stealing MOVE events
  and sending CANCEL to child Pressables.
- In JS (MarkerView.tsx), receive the position event and apply the translation
  as a React transform on RNMBXMarkerView, keeping the Fabric shadow tree in
  sync with Mapbox's native positioning so hit regions are correct.
- Register the event via AbstractEventEmitter in RNMBXMarkerViewContentManager.
- Add an interactive example (sliders, switch, counter, TextInput, Pressable)
  to the MarkerView example to exercise complex interactivity.

iOS is unaffected: Mapbox iOS positions annotations via frame assignment, which
Fabric tracks correctly without any workaround.

* refactor(markerView): simplify and fix type errors from review

- Make anchor/allowOverlap/allowOverlapWithPuck/isSelected props optional
  (restores defaultProps behaviour lost in class→function conversion; fixes
  TypeScript errors in CustomCallout, Markers.tsx, and example)
- Fix non-null assertions on positional array accesses in example
- Remove dead legacy iOS pre-v10 PointAnnotation fallback and associated
  imports (NativeModules, Platform, PointAnnotation, _nextMarkerViewId,
  compatIdRef) — MapboxV10 is always truthy on supported SDK versions
- Move PixelRatio.get() to module-level constant (device-constant value)
- Add JS-side position dedup (mirrors Kotlin-side dedup) to avoid re-renders
  when native re-applies the same translation
- Wrap onTouchEnd and onAnnotationPosition in useCallback for stable refs
- Replace custom AnnotationPositionEvent class with existing BaseEvent from
  com.rnmapbox.rnmbx.components.camera to eliminate duplication
- Guard requestDisallowInterceptTouchEvent to ACTION_DOWN only — Android
  resets the disallow flag on each new gesture, so subsequent MOVE/UP calls
  were redundant

* perf(markerView): stabilise all inline prop values

- Extract DEFAULT_ANCHOR constant — default object literal was recreating
  {x:0.5, y:0.5} on every render when no anchor was passed
- Memoize nativeCoordinate with useMemo([coord[0], coord[1]]) — avoids a
  new array on every render when coordinate values haven't changed
- Memoize nativeStyle with useMemo([style, annotationTranslate]) — avoids
  a new array (and inner object) every render
- Move inline style objects to StyleSheet.create (absolutePosition,
  contentContainer) — stable references, processed once by the native layer

* style: format CodegenTypes import per prettier

* fix(specs): keep CodegenTypes import single-line so @ts-ignore suppresses CI typecheck error

* fix(codegen): restore Float type in OnAnnotationPositionEvent for RN codegen parser

* docs: regenerate MarkerView.md after JSDoc update

* docs: regenerate docs/examples.json after children prop description update
* feat: add Snow and Rain particle effect components

Implements <Snow /> and <Rain /> components backed by Mapbox Maps SDK
v11.9+ experimental precipitation APIs (@_spi(Experimental)).

- Add snow/rain to the code generator (generateCodeWithEjs.mjs, globals.mjs)
- Generate SnowLayerStyleProps / RainLayerStyleProps TypeScript types
- Add RNMBXSnow / RNMBXRain iOS singleton layer implementations
- Add RNMBXSnow / RNMBXRain Fabric component views (iOS)
- Add RNMBXSnow / RNMBXRain Android implementations + managers
- Register components in RNMBXPackage and package.json codegenConfig
- Export Snow and Rain from src/Mapbox.native.ts
- Add SnowAndRain example (V11) and integrate into TerrainSkyAtmosphere
- Bump Mapbox SDK iOS + Android to 11.20.1
- Work around MapContentReconciler reset of rain by async re-applying
  after initial style load completes

* fix: set explicit Rain color/vignetteColor to avoid measure-light expression errors

The default color and vignetteColor for Rain use measure-light(brightness)
expressions which fail with 'Brightness is unavailable in the current
evaluation context' in terrain/atmosphere map styles. Set explicit static
colors to bypass the failing expressions.

* docs: note measure-light limitation in Rain component JSDoc

* fix: use Standard style in TerrainSkyAtmosphere so Rain measure-light works natively

* fix: deduplicate shared property names in styleMap.ts, add precommit script

* fix: add Snow and Rain to public interface test

* chore: regenerate generated files (new style spec properties)

* fix: correct Snow/Rain import paths and remove unsupported rasterElevationReference

Fix Android build by using precipitations.generated package path for
Snow/Rain. Remove rasterElevationReference (not in SDK 11.20.1 despite
spec claiming 11.19.0). Add measure-light availability warning to Rain.

* fix: use getStyleLights() method instead of styleLights property
@mariusud mariusud closed this Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants