You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add userTrackingMode: follow and course tracking that behave the same on every provider #185
Part of #183 (live navigation, phase 2). Builds on #184.
Problem
followsUserLocation behaves differently on every provider:
Apple: it sets MapKit's .follow mode, and MapKit drops that mode as soon as the user pans. JS is not told, the prop stays true, and tracking stays off until the prop changes.
No provider can turn the camera with the direction of travel, which is what a navigation camera needs most.
Proposed API
typeUserTrackingMode='none'|'follow'|'course'|'compass';/** How the camera follows the user. Needs `showsUserLocation`. */
userTrackingMode?: UserTrackingMode;/** Zoom and pitch to hold while tracking. A value that is left out keeps the current camera's value. */
userTrackingCamera?: {zoom?: number; pitch?: number};/** Called when the map changes the tracking mode on its own: today, only to 'none' after a user gesture. */
onUserTrackingModeChange?: (mode: UserTrackingMode)=>void;
follow keeps the user at the camera target and leaves the heading alone.
course does the same and rotates the map so that the direction of travel points up. While the course is unknown, for example when standing still, it keeps the last one instead of snapping to north.
compass rotates the map with the device heading. MapKit has this built in (.followWithHeading). The Google Maps SDKs need the device's heading sensor, so this mode can come after the other two.
The camera target honours mapPadding; that is how an app puts the user low on screen to show the road ahead. Google Maps already moves the target with the padding. For MapKit, check whether .follow honours layoutMargins, and if it does not, follow manually with MKMapCamera. course is manual on MapKit either way, because MapKit has no course mode.
On every provider, a user gesture (pan, zoom, rotate or tilt) ends tracking. The native side switches to none and calls onUserTrackingModeChange('none'). The app stores that in its state and sets the mode again, for example from a "re-center" button.
This contract has to be documented. If JS keeps passing 'course' after the native side dropped to none, the prop is not sent again, because from React's point of view it did not change.
Alternative considered: imperative ref.startUserTracking(mode, options) and ref.stopUserTracking() methods plus the same event. That is where @rnmapbox/maps ended up (Viewport.transitionTo and idle, onStatusChanged). A prop fits this library's camera and region props better; revisit if keeping the state in sync turns out to be error-prone.
Update rate and smoothness
While tracking, Android should ask for updates about once a second (see #184). Each fix moves the camera from native code, with no JS round trip. Check smoothness at one update per second on a simulated drive. On Android, animateCamera takes a duration but no easing curve, and a new animation cancels the one in progress.
Native mapping
Provider
follow
course
compass
Detecting a user gesture
Apple
.follow, or manual MKMapCamera
manual MKMapCamera with the location's course as heading
.followWithHeading
mapView(_:didChange:animated:) for MapKit's own modes; isUserInteracting in regionWillChangeAnimated for the manual ones
Google iOS
camera animation on the myLocation KVO that already exists
Unit tests for the mode state machine and for holding the last course, as pure Kotlin and Swift.
At runtime: "Freeway Drive" in the iOS Simulator on both iOS providers and GPX playback on the Android emulator, with a manual pan during tracking on each provider.
Not in scope
A navigation-style user marker, zoom that changes with speed, look-ahead beyond mapPadding, and snapping to the route. These are listed under "Later" in #183.
Part of #183 (live navigation, phase 2). Builds on #184.
Problem
followsUserLocationbehaves differently on every provider:.followmode, and MapKit drops that mode as soon as the user pans. JS is not told, the prop staystrue, and tracking stays off until the prop changes.myLocationupdate, even right after the user panned, so the map fights the finger. It never rotates, and it also turns on the my-location button (Add myLocationButtonEnabled instead of coupling the button to followsUserLocation #112).No provider can turn the camera with the direction of travel, which is what a navigation camera needs most.
Proposed API
followkeeps the user at the camera target and leaves the heading alone.coursedoes the same and rotates the map so that the direction of travel points up. While the course is unknown, for example when standing still, it keeps the last one instead of snapping to north.compassrotates the map with the device heading. MapKit has this built in (.followWithHeading). The Google Maps SDKs need the device's heading sensor, so this mode can come after the other two.mapPadding; that is how an app puts the user low on screen to show the road ahead. Google Maps already moves the target with the padding. For MapKit, check whether.followhonourslayoutMargins, and if it does not, follow manually withMKMapCamera.courseis manual on MapKit either way, because MapKit has no course mode.followsUserLocation={true}becomes a deprecated alias foruserTrackingMode="follow", and the Android warning added for Android silently ignores followsUserLocation and showsScale #104 goes away.Gestures
On every provider, a user gesture (pan, zoom, rotate or tilt) ends tracking. The native side switches to
noneand callsonUserTrackingModeChange('none'). The app stores that in its state and sets the mode again, for example from a "re-center" button.This contract has to be documented. If JS keeps passing
'course'after the native side dropped tonone, the prop is not sent again, because from React's point of view it did not change.Alternative considered: imperative
ref.startUserTracking(mode, options)andref.stopUserTracking()methods plus the same event. That is where @rnmapbox/maps ended up (Viewport.transitionToandidle,onStatusChanged). A prop fits this library'scameraandregionprops better; revisit if keeping the state in sync turns out to be error-prone.Update rate and smoothness
While tracking, Android should ask for updates about once a second (see #184). Each fix moves the camera from native code, with no JS round trip. Check smoothness at one update per second on a simulated drive. On Android,
animateCameratakes a duration but no easing curve, and a new animation cancels the one in progress.Native mapping
followcoursecompass.follow, or manualMKMapCameraMKMapCamerawith the location'scourseas heading.followWithHeadingmapView(_:didChange:animated:)for MapKit's own modes;isUserInteractinginregionWillChangeAnimatedfor the manual onesmyLocationKVO that already existsbearingset to the courseCLLocationManagerheading updatesmapView(_:willMove:)withgesture == trueanimateCamerafromFusedLocationSourcebearingset to the courseOnCameraMoveStartedListener.REASON_GESTURE, already wiredRelated
followsUserLocation. Keep that for the alias, and decide here or in Add myLocationButtonEnabled instead of coupling the button to followsUserLocation #112 whetheruserTrackingModeaffects the button.followworks on Android, remove the warning aboutfollowsUserLocation.Acceptance criteria
followandcoursebehave the same on Apple, Google iOS and Google AndroidonUserTrackingModeChange('none')oncecoursekeeps the last known course while standing stillmapPaddingon every providershowsUserLocationturns off or permission is revokedfollowsUserLocationstill works as an alias, and its Android warning from Android silently ignores followsUserLocation and showsScale #104 is goneTesting
Not in scope
A navigation-style user marker, zoom that changes with speed, look-ahead beyond
mapPadding, and snapping to the route. These are listed under "Later" in #183.