24

Does anyone know of a way to change the placing of the compass in MKMapview?

I'm talking about the compass that shows up in the upper right corner of the map when it's rotated.

I'm making it so you can see the map blurred underneath the navigation bar, which means the frame's y origin will be underneath the nav-bar. The problem is that when the map is rotated, it automatically adds the compass button in the upper right corner and as you can (kind of) see in the screenshot below, that's covered by the navBar.

screenshot

I haven't seen any documentation talking about the the compass, anybody have any ideas?

GetSwifty
  • 7,568
  • 1
  • 29
  • 46

3 Answers3

17

My tests have shown, that the MKMapView somehow knows in what context it is displayed. If you use a regular UINavigationController you'll get the compass positioned below the navigation bar even when it is translucent.

There is a new UIViewController property in iOS 7 that defines the beginning of the actual content on the Y axis. It is called topLayoutGuide.

Sadly you cannot really influence the value that Apple uses to calculate this property in different contents. It also depends on the visibility of the status bar.

BUT: To solve this problem, I created a simple custom object that I return in my custom view controller:

@interface MiFixedLayoutGuide : NSObject <UILayoutSupport>
@property (nonatomic) CGFloat pbLength;
- (id)initWithLength:(CGFloat)length;
@end

@implementation MiFixedLayoutGuide

- (id)initWithLength:(CGFloat)length {
    self = [super init];
    if (self) {
        _pbLength = length;
    }
    return self;
}

- (CGFloat)length {
    return _pbLength;
}

@end

Then I overrode the following two methods in my UIViewController subclass:

- (id<UILayoutSupport>)topLayoutGuide {
    return [[MiFixedLayoutGuide alloc]initWithLength:44];
}

- (id<UILayoutSupport>)bottomLayoutGuide {
    return [[MiFixedLayoutGuide alloc]initWithLength:44];
}

This insets the layout guides by 44 points. And the MKMapView will use these values to position the compass at the top, and the "Legal" label at the bottom.

One thing that you now have to pay attention to is, that you cannot use the layout guides anymore within the auto layout visual format language.

KlimczakM
  • 12,576
  • 11
  • 64
  • 83
Klaas
  • 22,394
  • 11
  • 96
  • 107
  • A small addition: MKMapView also uses the layout guides to define the center of the map. – Klaas Sep 19 '13 at 20:28
  • Works great if my map view is directly in view controller's view, but seems to ignore these values if it's one level deeper in the hierarchy. Where do I have to put this code if my view has a scroll view which contains the map? – gklka Jan 19 '14 at 15:07
  • 1
    @gklka haven't tested what you are asking for. But my experience is that `UIScrollView`s are 'evil' in regards to layout guides and map views. Let me know when you find a solution and I'll update my answer. – Klaas Jan 19 '14 at 15:18
  • Your solution works great for me, though adding a right margin seems not to work. Any ideas? I tried: - (id)rightLayoutGuide { ... } – electronix384128 Apr 22 '14 at 10:11
  • 2
    @Klaas why did you have to create the class MiFixedLayoutGuide? Why not just make the view controller containing the mapView conform to . And then implement length() in there with a return value of 44. And of coarse for topLayoutGuide() and bottomLayoutGuide you return self (the view controller containing the map). I see no need for the class MiFixedLayoutGuide really.. – pnizzle Jul 25 '14 at 04:20
  • While it may seem 'extreme' to split off a single property into a separate class, it is good design and hits three of the SOLID principles. FWIW Swift makes splitting off these utility classes much less verbose. – Christopher Swasey Dec 22 '14 at 15:50
16

With iOS 8 you can simple do:

[someMapView setLayoutMargins:UIEdgeInsetsMake(20, 0, 20, 0)];
Monobono
  • 780
  • 10
  • 24
  • 1
    But is there is issue with "Legal" lable on map after implementing this solution. "Legal" lable on map also gets moved if we change it for compass. In my case when I tried to change position for compass then "Legal" lable also gets moved and displayed at center of map instaed of its defalut (Left most bottom corner) also is there any way to bring compass on top (bring to front) if its overlapped with any other component added on map. ie. button on map – iLearner Apr 27 '16 at 12:23
6

Swift:

mapView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)

Where instead of 20 you could calculate your UI elements height to have a more dynamic solution adjusting to each device or element.

Juan Boero
  • 6,281
  • 1
  • 44
  • 62