iOS - MKMapView showAnnotations:animated: with padding?
Asked Answered
A

4

14

I want to be able to zoom my MKMapView to fit it's annotations. I have managed to do it using iOS7's showAnnotations method. But I would also like to add some padding or inset from the map view border. This is because I have a semi-transperant view that overlays the top part of my map, and I don't want annotations to be places behind this view. I have tried this:

[self.mapView showAnnotations:annotations animated:YES];
[self.mapView setVisibleMapRect:self.mapView.visibleMapRect edgePadding:UIEdgeInsetsMake(100, 20, 10, 10) animated:NO];

But it's not working as I would have hoped. Any ideas on how I could do it differently?

Americanist answered 20/2, 2014 at 15:33 Comment(0)
D
13

You are doing it the right way. Try changing the padding, you'll see the difference.

Other way, there must be something else in your code preventing from changing the view

EDIT: I was totally wrong. Try this:

Create an instance variable

BOOL _mapNeedsPadding;

and initialize it to NO;

Then set your mapView delegate to self and add a to your class header

Then add this to your class

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
    if(_mapNeedsPadding){
        _mapNeedsPadding = NO;
        [self.mapView setVisibleMapRect:self.mapView.visibleMapRect edgePadding:UIEdgeInsetsMake(100, 20, 10, 10) animated:YES];
    }
}

And finally call your showAnnotations function like this:

_mapNeedsPadding = YES;
[self.mapView showAnnotations:annotations animated:YES];

The showAnnimation will trigger the regionDidChangeAnimated function. You need to set _mapNeedsPadding to NO after changing visibleMapRect because this function (setVisibleMapRect:self) will also trigger regionDidChangeAnimated.

Hope this helps !

Derose answered 26/2, 2014 at 13:30 Comment(1)
Great answer! you may want to set _mapNeedsPadding = NO before calling setVisibleMapRect: on your mapView. otherwise it leads to a recursive loop that'll lead to a bad access error (I have edited the answer to reflect the same. Let me know if you disagree with this edit)Hindsight
S
19

You could also simply use

[self.mapView showAnnotations:annotations animated:YES];
self.mapView.camera.altitude *= 1.4;

to zoom out a little. Works well for me.

Sandpit answered 20/7, 2014 at 16:44 Comment(2)
Works for me even after almost an year later :)Gerek
Work ok, but once I change the altitude, the annotation is no longer centeredChromaticity
G
16

Starting with iOS8, MKMapView has a layoutMargin property. When this is set, centerRegion:, showAnnotations: and all methods that try to fit a rectangle within the map view will take into account the aforementioned layout margins.

If your translucent view is 40 points height and attached to the top of the map view, settings mapView.layoutMargin = UIEdgeInsetMake(40, 0, 0, 0) will do the magic.

If targeting iOS7, the map view uses the top and bottom layout guide of its containing controller to also offset its content. So you could override the topLayoutGuide method of the controller to return the desired length.

class ViewController: UIViewController {
      override var topLayoutGuide: UILayoutSupport {
        return MapLayoutGuide(length: 40)
    }
}


class MapLayoutGuide: NSObject, UILayoutSupport {
    var length: CGFloat

    init(length: CGFloat) {
        self.length = length
        super.init()
    }

    @available(iOS 9.0, *)
    var bottomAnchor: NSLayoutYAxisAnchor {
        return NSLayoutYAxisAnchor()
    }
    @available(iOS 9.0, *)
    var topAnchor: NSLayoutYAxisAnchor {
        return NSLayoutYAxisAnchor()
    }
    @available(iOS 9.0, *)
    var heightAnchor: NSLayoutDimension {
        return NSLayoutDimension()
    }
}
Goatsbeard answered 19/10, 2015 at 13:8 Comment(5)
I was looking for this layoutMargin everywhere and after giving up, tried to implement it myself. Thanks to you now it is working as it shouldFormal
It definitely is inspecting the class because it needs to be identical to this to be accepted by the mapViewKandi
This will also move the Apple Maps "Legal" button at the bottom-left side of the map, not a good solution for meErdda
I was using this but it crashed when I attempted to constrain a view to that bottom anchor, I don't think the anchors are supposed to be init like how they are in those getters.Kandi
In addition to Aluminium's response - if you rotate the map view's view so that north isn't facing directly upwards, this also indents the circular compass icon shown by the map to indicate heading.Macintosh
D
13

You are doing it the right way. Try changing the padding, you'll see the difference.

Other way, there must be something else in your code preventing from changing the view

EDIT: I was totally wrong. Try this:

Create an instance variable

BOOL _mapNeedsPadding;

and initialize it to NO;

Then set your mapView delegate to self and add a to your class header

Then add this to your class

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
    if(_mapNeedsPadding){
        _mapNeedsPadding = NO;
        [self.mapView setVisibleMapRect:self.mapView.visibleMapRect edgePadding:UIEdgeInsetsMake(100, 20, 10, 10) animated:YES];
    }
}

And finally call your showAnnotations function like this:

_mapNeedsPadding = YES;
[self.mapView showAnnotations:annotations animated:YES];

The showAnnimation will trigger the regionDidChangeAnimated function. You need to set _mapNeedsPadding to NO after changing visibleMapRect because this function (setVisibleMapRect:self) will also trigger regionDidChangeAnimated.

Hope this helps !

Derose answered 26/2, 2014 at 13:30 Comment(1)
Great answer! you may want to set _mapNeedsPadding = NO before calling setVisibleMapRect: on your mapView. otherwise it leads to a recursive loop that'll lead to a bad access error (I have edited the answer to reflect the same. Let me know if you disagree with this edit)Hindsight
C
0

another solution:

extension MKMapView
{
    func fitAllMarkers(inset: UIEdgeInsets? = nil) {
        self.showAnnotations(self.annotations, animated: false)
        OperationQueue.main.addOperation {
            self.setVisibleMapRect(self.visibleMapRect, edgePadding: inset ?? UIEdgeInsets.zero, animated: true)
        }
    }
}
Cilice answered 23/11, 2018 at 9:27 Comment(1)
This doesn't animate from the initial view to the final view, so not ideal for me.Flung

© 2022 - 2024 — McMap. All rights reserved.