Janak Nirmal
Janak Nirmal

Reputation: 22726

how to check if Marker is in the region displayed on the screen in react native maps

I have list of items displayed horizontally in FlatList at the bottom and all the markers in the map. Now user can zoom in/out map freely or click on any marker and the bottom FlatList is scrolling to item. User can also scroll FlatList and based on the index marker is selected. When the list is large and user has manually zoom in the map.

Now problem is user can zoom any where on map and when user swipes List below and changes the item index, I need to be able to check if that marker is on the screen or not and if not zoom to that marker.

so for I was able to zoom manually with region with below code.

  const animateMapToRegion = (region = null, duration = 1000) => {
    if (region) {
      mapRef.current.animateToRegion(region, duration);
    }
  };

This animation only needs to be done if marker is not already on screen. I checked documentation and was not able to find any function to check whether marker is on the screen or not.

I want to animate only if marker is not visible on screen. Please assist.

Upvotes: 2

Views: 1360

Answers (1)

Janak Nirmal
Janak Nirmal

Reputation: 22726

I ended up by adding my own implementation for both iOS and Android.

Add below method in AIRGoogleMapManager.m

RCT_EXPORT_METHOD(isMarkerWithinScreen:(nonnull NSNumber *)reactTag
                  position:(CLLocationCoordinate2D)position
                  resolver: (RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
  [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
    id view = viewRegistry[reactTag];
    if (![view isKindOfClass:[AIRGoogleMap class]]) {
      RCTLogError(@"Invalid view returned from registry, expecting AIRMap, got: %@", view);
    } else {
      AIRGoogleMap *mapView = (AIRGoogleMap *)view;

      GMSVisibleRegion region = [mapView.projection visibleRegion];
      GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:region];
        
      resolve(@{@"visible": @([bounds containsCoordinate:position])});
    }
  }];
}

Add below method in AirMapModule.java

 @ReactMethod
  public void isMarkerWithinScreen(final int tag, ReadableMap coordinate, final Promise promise) {
    final ReactApplicationContext context = getReactApplicationContext();

    final double lat = coordinate.getDouble("latitude");
    final double lng = coordinate.getDouble("longitude");
    final LatLng position = new LatLng(lat,lng);

    UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
    uiManager.addUIBlock(new UIBlock()
    {
      @Override
      public void execute(NativeViewHierarchyManager nvhm)
      {
        AirMapView view = (AirMapView) nvhm.resolveView(tag);
        if (view == null)
        {
          promise.reject("AirMapView not found");
          return;
        }
        if (view.map == null)
        {
          promise.reject("AirMapView.map is not valid");
          return;
        }

        LatLngBounds currentScreen = view.map.getProjection().getVisibleRegion().latLngBounds;
        boolean onScreen = currentScreen.contains(position);

        WritableMap visibleJson = new WritableNativeMap();
        visibleJson.putBoolean("visible", onScreen);

        promise.resolve(visibleJson);
      }
    });
  }

add finally add below in node_modules/react-native-maps/lib/components/MapView.js

isMarkerWithinScreen(marker) {
    if (Platform.OS === 'android') {
      return NativeModules.AirMapModule.isMarkerWithinScreen(
        this._getHandle(),
        marker.coordinate
      );
    } else if (Platform.OS === 'ios') {
      return this._runCommand('isMarkerWithinScreen', [marker.coordinate]);
    }
    return Promise.reject('isMarkerWithinScreen not supported on this platform');
  }

Now you can use this as,

  const marker = //your marker;
  const visibility = await mapRef.current.isMarkerWithinScreen(marker);
  const {visible} = visibility;
  console.log('visible-->', visible); //If visible is true marker is on screen otherwise its not.

Upvotes: 1

Related Questions