rnmapbox / maps

A Mapbox react native module for creating custom maps
MIT License
2.22k stars 838 forks source link

[Bug]: Android Compass never renders when `compassEnabled` & `compassFadeWhenNorth` initially set #2875

Open KiwiKilian opened 1 year ago

KiwiKilian commented 1 year ago

Mapbox Implementation

Mapbox

Mapbox Version

default

Platform

Android

@rnmapbox/maps version

main (10.0.7-rc.0)

Standalone component to reproduce

import { Camera, MapView } from '@rnmapbox/maps';
import React from 'react';

const styles = {
  mapView: { flex: 1 },
};

const BugReportExample = () => {
  return (
    <>
      <MapView style={styles.mapView} compassEnabled compassFadeWhenNorth>
        <Camera
          defaultSettings={{
            centerCoordinate: [-74.00597, 40.71427],
            zoomLevel: 14,
          }}
        />
      </MapView>
    </>
  );
};

export default BugReportExample;

Observed behavior and steps to reproduce

When compassEnabled and compassFadeWhenNorth are initially enabled, the compass is not rendered when rotating the map. It seems to be different, when the prop is enabled after initial rendering, see Ornaments example for comparison.

Expected behavior

The compass should appear, when rotating the map.

Notes / preliminary analysis

Maybe related to #2543.

Additional links and references

No response

KiwiKilian commented 1 year ago

Our current workaround is:

const BugReportExample = () => {
  const [loaded, setLoaded] = useState(false);

  return (
    <MapView
      style={styles.mapView}
      compassEnabled
      compassFadeWhenNorth={loaded}
      onDidFinishLoadingMap={() => setLoaded(true)}
    >
      <Camera
        defaultSettings={{
          centerCoordinate: [-74.00597, 40.71427],
          zoomLevel: 14,
        }}
      />
    </MapView>
  );
};
andrei-tofan commented 1 year ago

I tested v10.0.12 on Android and the issue is still present. Can this be fixed?

ChimiChumi commented 6 months ago

Issue still persists, currently this is how I made a workaround:

const [isCompassVisible, setCompassVisible] = useState(false);
...
...

const handleMapLoad = e => {
    setCompassVisible(true);
  };

<MapView
   ...
   ...
   ...
   compassEnabled={isCompassVisible}
   onDidFinishLoadingMap={handleMapLoad}>

Didn't try it together with compassFadeWhenNorth. Currently, this solution works all the time.

brien-crean commented 2 months ago

You could also just create your own compass button. That way you can customize the way it looks. E.g.

interface CompassButtonProps {
  mapState: MapState | undefined;
  cameraRef: React.MutableRefObject<Camera | null>;
}

const CompassButton: React.FC<CompassButtonProps> = ({
  cameraRef,
  mapState,
}) => {
  const onCompassPress = () => {
    cameraRef.current?.setCamera({
      heading: 0, // reset camera heading
      animationDuration: 200,
    });
  };

  if (!mapState || !mapState.properties.heading) {
    return null; // disappears when heading is 0
  }

  return (
    <Pressable
      onPress={onCompassPress}
      style={{
        transform: [
          {
            rotate: `${-mapState.properties.heading}deg`,
          },
        ],
      }}
    >
      <Icon name={'compass'} size={30} />
    </Pressable>
  );
};

mapState comes from the onCameraChanged MapView callback


const handleOnCameraChanged = (mapState: MapState) => {
    setMapState(mapState);
  };

<MapView
  onCameraChanged={handleOnCameraChanged}
>
...
</MapView>
SethArchambault commented 2 weeks ago

Issue still persists, currently this is how I made a workaround:

const [isCompassVisible, setCompassVisible] = useState(false);

const handleMapLoad = e => {
    setCompassVisible(true);
  };
...
<MapView
   ...
   ...
   ...
   compassEnabled={isCompassVisible}
   onDidFinishLoadingMap={handleMapLoad}>

Didn't try it together with compassFadeWhenNorth. Currently, this solution works all the time.

I tried this with compassFadeWhenNorth but it seems that if compassVisible is set to true when you start up the app and you're in the north position, then it still won't work.

I got around it by turning compassEnabled + compassFadeWhenNorth on only when the camera changes and it's heading isn't zero. Looks like that will make the compass show up, and then compassFadeWhenNorth will hide it when heading is zero again.

  const [compassEnabled, setCompassEnabled,] = useState(false)
  const onCameraChanged = useCallback(
    ({ properties, gestures, }: MapState) => {
      if (properties.heading != 0) {
        setCompassEnabled(true)
      }
    }
)
...
    <MapboxGL.MapView
      onCameraChanged={onCameraChanged}
      compassEnabled={compassEnabled}
      compassFadeWhenNorth={compassEnabled}

Weird little hack!