mapbox-gl-native icon indicating copy to clipboard operation
mapbox-gl-native copied to clipboard

MGLMapView setCamera( <camera fitting content>() ) doesn't work correctly with MGLMapView.contentInset

Open astojilj opened this issue 6 years ago • 3 comments

Following methods:

  • (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds;
  • (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets;
  • (MGLMapCamera *)camera:(MGLMapCamera *)camera fittingCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets;
  • (MGLMapCamera *)camera:(MGLMapCamera *)camera fittingShape:(MGLShape *)shape edgePadding:(UIEdgeInsets)insets;
  • (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets;

use currently set MGLMapView.contentInset when computing camera that would fit desired content to an area defined by MGLMapView.contentInset + optional padding, as if contentInset in MGLMapView would not additionally move the content when

setting this camera using any of following

@property (nonatomic, copy) MGLMapCamera *camera;

  • (void)setCamera:(MGLMapCamera *)camera animated:(BOOL)animated;
  • (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function;
  • (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
  • (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(UIEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion;
  • (void)flyToCamera:(MGLMapCamera *)camera completionHandler:(nullable void (^)(void))completion;
  • (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration completionHandler:(nullable void (^)(void))completion;
  • (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration peakAltitude:(CLLocationDistance)peakAltitude completionHandler:(nullable void (^)(void))completion;

As a result we get double contentInset applied.

Workaround:

set view contentInset to .zero (set automaticallyAdjustsScrollViewInsets to false) and supply desired padding to calls, e.g.:

contentInset = .zero
let camera = cameraThatFitsShape(line, direction: direction, edgePadding: safeArea)
setCamera(camera, animated: animated)

astojilj avatar Sep 06 '19 06:09 astojilj

I agree that there’s a difference in behavior between the -camera… getters and the -setCamera… setters and that it’s confusing and unexpected. To be clear, the desired behavior is that the getters’ use of content insets cancel out the setters’ use of content insets. For example, this code should have no effect even when there’s a content inset:

mapView.camera = mapView.camera

equivalently:

[mapView setCamera:[mapView camera]];

At the same time, if you pass the return value of -cameraThatFitsCoordinateBounds:edgePadding: into -setCamera:withDuration:animationTimingFunction:edgePadding:completionHandler:, you would have to pass the same edge padding into both methods in order for the -cameraThatFitsCoordinateBounds:edgePadding: return value to be accurate. At least that’s how I’d expect the methods to behave based on their naming.

1ec5 avatar Sep 16 '19 21:09 1ec5

I spended near 8h try to resolve bug related to this behaviour

@1ec5 thanks for clarifying, it's unfortunate that this part of the API is a often misunderstood. Finding these illuminating threads sure feels like collecting golden nuggets.

I am currently setting my camera with the following to prevent any sudden jumps:

mapView.setCamera(
    mapView.camera(
        mapView.camera,
        fitting: .init(
            center: center,
            distance: 1000 // meters
        ),
        edgePadding: padding // matching padding
    ),
    withDuration: 1,
    animationTimingFunction: nil,
    edgePadding: padding, // matching padding
    completionHandler: nil
)

Sometimes, though, the edgePadding is not preserved and the camera jumps. Could you suggest some way to fix this? It happens when the user changes the map zoom level by panning. It looks like this resets the edgePadding on the camera, and setting a camera with added edge padding again will cause it's viewport to jump.

ferologics avatar May 19 '21 21:05 ferologics