Question

I have to show an MKMapRoute on an MKMapView and it has to be shown in a specific region on my map, not in the center but in the top area. It must be placed in a "frame" of about 80 points of height.

I thought I would be able to achieve this by using MKMapView's method

- (MKMapRect)mapRectThatFits:(MKMapRect)mapRect edgePadding:(UIEdgeInsets)insets

but I'm facing some oddities. Precisely, if I use this snippet of code:

//obj is the returned mkroute object.
MKRoute *rout = obj;        
MKPolyline *line = [rout polyline];
MKMapRect boundingMapRect = [line boundingMapRect];
MKMapRect fittedRect = [self.mapView mapRectThatFits:[line boundingMapRect] 
                               edgePadding:UIEdgeInsetsMake( 100, 0, 0, 0)];
MKCoordinateRegion r = MKCoordinateRegionForMapRect(fittedRect);
[self.mapView setRegion: r animated:YES];

the result is visible in the following screenshot:

MKMapRect with 100 points top inset

However if I set the edge insets to be (0,0,0,0) by changing the fourth line of my previous snippet with:

MKMapRect fittedRect = [self.mapView mapRectThatFits:[line boundingMapRect]  
                               edgePadding:UIEdgeInsetsMake( 100, 0, 0, 0)];

the result is this one:

MKMapRect with 0 points top inset

As you can clearly see, the offset has changed of 50 (and not 100) points from the top. Things seem to get even more weird when I play with span latitude and longitude delta of the MKMapRegion, or change the device screen.

Now, my aim is simply to tell mapView to draw that route in a frame of say "64 points from the top and with an height of 80 points and a width of 320 points", I'm not finding an easy way to do this and I'm afraid I'm missing something. I don't want to play by altering region's latitude because content size may vary (there could be a smaller or a bigger route to show). What Am I missing and what could be an easy way to do this? why the insets are not being calculated properly?

Was it helpful?

Solution

By specifying only the top padding, the map view tries to maximize the displayed size of the overlay in the space left over without distorting the map and based on zoom levels it can present. So you don't get close to the desired width and height that you want.

You can control the displayed position of the overlay better if you calculate and specify the padding for all the sides instead of just the top.

However, the map view will still adjust the actually displayed region based on what it can present without distorting the map, etc. but the result should be closer to what you want.

Based on this sentence in your last paragraph:

my aim is simply to tell mapView to draw that route in a frame of say "64 points from the top and with an height of 80 points and a width of 320 points"

here is an example of how you could get close to that result:

MKRoute *rout = obj;        
MKPolyline *line = [rout polyline];

CGFloat desiredDistanceFromTop = 64.0;
CGFloat desiredWidth = 320.0;
CGFloat desiredHeight = 80.0;

CGFloat topPadding = desiredDistanceFromTop;
CGFloat leftPadding = (self.mapView.frame.size.width - desiredWidth) / 2.0;
CGFloat rightPadding = leftPadding;
CGFloat bottomPadding = self.mapView.frame.size.height 
                            - topPadding 
                            - desiredHeight;

UIEdgeInsets edgeInsets = 
    UIEdgeInsetsMake(topPadding, leftPadding, bottomPadding, rightPadding);

[self.mapView setVisibleMapRect:line.boundingMapRect 
                    edgePadding:edgeInsets 
                       animated:YES];

Separately, note that it's not necessary to convert the map rect to a region or to calculate a "fitted" rect/region yourself since:

  • You can use setVisibleMapRect instead of setRegion.
  • Both setVisibleMapRect and setRegion will themselves calculate a "fitted" rect/region given the requested rect/region.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top