9

Using the Google Maps for iOS SDK, the "My Location" button is by default placed in the bottom right hand corner:

enter image description here

I'd like to place it in the bottom left hand corner (yes, I realize I need to be careful not to obscure the "Google" logo there). I don't believe the SDK has an "official" way of doing this, but based on this answer I have figured out how to get a hold of the My Location button subview so that I can position it.

The part that has me confused is what values I should be giving to the My Location view's frame and/or bounds to get it where I want it. For starters, the My Location button as it currently stands has frame.origin.x = -74 and frame.origin.y = -54. This is the first time I've seen negative coordinates for a frame.origin.x or a frame.origin.y and I'm not even sure how iOS handles negative coordinates. My first thought was that e.g. frame.origin.x = -74 is equivalent to [view superview].frame.size.width - 74, i.e. negative value are subtracted from the width or height of the superview. But then I looked at the width and height of the superview and they're both 0.0. Here's my code which outputs some information about both the map and the my location button frames and bounds:

- (void)loadView {
    GMSCameraPosition *cam = [GMSCameraPosition cameraWithLatitude:jcuTownsvilleCenterCampusLat longitude:jcuTownsvilleCenterCampusLon zoom:17];
    self.campusMap = [GMSMapView mapWithFrame:CGRectZero camera:cam];
    self.campusMap.myLocationEnabled = YES;
    self.campusMap.settings.myLocationButton = YES;
    self.view = self.campusMap;

    for (UIView *view in self.campusMap.subviews) {
        NSLog(@"view.description: %@",view.description);
        if ([view isKindOfClass:[UIButton class]]) {
            // these four values in the conditional below are just what happen to
            // be the values corresponding to the "my location button" in Google Maps
            // for iOS SDK version 1.3.0.3430.  They might change over time, so this
            // code is somewhat fragile.
            if (view.frame.size.width == 76 && view.frame.size.height == 54 &&
                view.frame.origin.x == -76 && view.frame.origin.y == -54) {
                NSLog(@"we may have found the 'my location' button");

                NSLog(@"self.campusMap coord stats:");
                NSLog(@"bounds.origin.x: %f", self.campusMap.bounds.origin.x);
                NSLog(@"bounds.origin.y: %f", self.campusMap.bounds.origin.y);
                NSLog(@"bounds.size.width: %f", self.campusMap.bounds.size.width);
                NSLog(@"bounds.size.height: %f", self.campusMap.bounds.size.height);
                NSLog(@"frame.origin.x: %f", self.campusMap.frame.origin.x);
                NSLog(@"frame.origin.y: %f", self.campusMap.frame.origin.y);
                NSLog(@"frame.size.width: %f", self.campusMap.frame.size.width);
                NSLog(@"frame.size.height: %f", self.campusMap.frame.size.height);

                NSLog(@"view coord stats:");
                NSLog(@"bounds.origin.x: %f", view.bounds.origin.x);
                NSLog(@"bounds.origin.y: %f", view.bounds.origin.y);
                NSLog(@"bounds.size.width: %f", view.bounds.size.width);
                NSLog(@"bounds.size.height: %f", view.bounds.size.height);
                NSLog(@"frame.origin.x: %f", view.frame.origin.x);
                NSLog(@"frame.origin.y: %f", view.frame.origin.y);
                NSLog(@"frame.size.width: %f", view.frame.size.width);
                NSLog(@"frame.size.height: %f", view.frame.size.height);
            }
        }
    }
}  

And here is the output:

self.campusMap coord stats:    
bounds.origin.x: 0.000000  
bounds.origin.y: 0.000000  
bounds.size.width: 0.000000  
bounds.size.height: 0.000000  
frame.origin.x: 0.000000  
frame.origin.y: 0.000000  
frame.size.width: 0.000000  
frame.size.height: 0.000000  
view coord stats:  
bounds.origin.x: 0.000000  
bounds.origin.y: 0.000000  
bounds.size.width: 76.000000  
bounds.size.height: 54.000000  
frame.origin.x: -76.000000  
frame.origin.y: -54.000000  
frame.size.width: 76.000000  
frame.size.height: 54.000000

I tried as a simple test to position the "My Location" button in the top left hand corner with:

CGRect frame = view.frame;
frame.origin.x = 0;
frame.origin.y = 0;
frame.size.width = 76;
frame.size.height = 54;
[view setFrame:frame];

but then the My Location button didn't show at all.

I have also tried small modifications to the existing values (e.g. changing frame.origin.x from -76.0 to -66.0 and can see the difference in position, so at least I'm confident I'm modifying the position of the right view. I still don't understand i) how negative coordinates work and ii) how to properly position the view in this specific scenario though. After reading the answers to this question I thought I had a reasonable grasp on view frames and bounds, but given that I haven't gotten this to work yet, apparently not.

Community
  • 1
  • 1
Bryce Thomas
  • 10,479
  • 26
  • 77
  • 126
  • In `loadView` you've just allocated the map view with a size of zero (which causes it to be resized to fill the screen later). It might be worth checking what all of the values are at some later point - eg in `viewDidLoad` or `viewDidAppear` or something like that? – Saxon Druce Jun 02 '13 at 06:26
  • It might also be worth checking the values of the button's `transform` property, to see if the transform is set to something unusual. – Saxon Druce Jun 02 '13 at 06:33

6 Answers6

20

The easiest way to solve this is to change the frame and autoresizing mask of the button right after the map view is created.

UIButton* myLocationButton = (UIButton*)[[mapView_ subviews] lastObject];
myLocationButton.autoresizingMask = UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleTopMargin;
CGRect frame = myLocationButton.frame;
frame.origin.x = 5;
myLocationButton.frame = frame;

EDIT: Sorry, I placed the button in the top right corner. Updated my code to place it in the bottom left hand corner.

Note: there is currently no API from Google to get the location button. The first line may not work in future releases of the SDK.

You also should create a feature request here.

Another option is to create the button yourself and add it as a subview to the map. The button handler code is fairly easy:

  CLLocation *location = mapView_.myLocation;
  if (location) {
    [mapView_ animateToLocation:location.coordinate];
  }
Felix
  • 35,354
  • 13
  • 96
  • 143
  • I'm marking this accepted as it does more or less what was asked. I haven't figured out how to get rid of the "padding" around the button yet though (e.g. try `frame.origin.x = 0` and it still sits a little way out from the corner). Setting `contentEdgeInsets`, `titleEdgeInsets` and `imageEdgeInsets` to `0.0` on the UIButton doesn't seem to quite do it either. – Bryce Thomas Jun 04 '13 at 03:42
  • 1
    Update: in version 1.4.0.4450 of the Google Maps iOS SDK the myLocation button has changed its position in the view hierarchy and so can no longer be accessed with the above mentioned `[[mapView_ subviews] lastObject]`. I now use... – Bryce Thomas Jul 17 '13 at 13:11
  • for (UIView *view in self.mapView.subviews) { NSString *viewClassString = NSStringFromClass([view class]); if ([viewClassString rangeOfString:@"GMSUISettingsView"].location != NSNotFound) { for (UIView *view2 in view.subviews) { if ([view2 isKindOfClass:[UIButton class]]) { _myLocationButton = (UIButton *)view2; } } } } – Bryce Thomas Jul 17 '13 at 13:12
10

You can use the padding of the GMSMapView.

GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.81969
                                                        longitude:144.966085
                                                             zoom:4];
_mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];

_mapView.settings.myLocationButton = YES;
_mapView.myLocationEnabled = YES;
_mapView.padding = UIEdgeInsetsMake(0, 0, kOverlayHeight, 0);
self.view = _mapView;

SWIFT 3

self._mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)

enter image description here

3lvis
  • 4,190
  • 1
  • 30
  • 35
6

in swift 3 xcode 8

func moveLocationButton() -> Void{
     for object in mapView.subviews{
        for obj in object.subviews{
           if let button = obj as? UIButton{
             let name = button.accessibilityIdentifier
                if(name == "my_location"){
                        //config a position
                        button.center = self.view.center 
                    }
                }
            }
        }
    }
2
for (UIView *object in mapView_.subviews) {
    if([[[object class] description] isEqualToString:@"GMSUISettingsView"] )
    {
        for(UIView *view in object.subviews) {
            if([[[view class] description] isEqualToString:@"UIButton"] ) {
                CGRect frame = view.frame;
                frame.origin.y -= 60;
                view.frame = frame;
            }
        }

    }
};
Waseem Sarwar
  • 2,645
  • 1
  • 21
  • 18
0

As of July 2015, this is a way to do it:

for (UIView *object in mapView_.subviews) {
    if([[[object class] description] isEqualToString:@"GMSUISettingsView"] )
    {
        for(UIView *view in object.subviews) {
            if([[[view class] description] isEqualToString:@"GMSx_QTMButton"] ) {
                CGRect frame = view.frame;
                frame.origin.y -= 60;
                view.frame = frame;
            }
        }

    }
};
Etienne Noël
  • 5,988
  • 6
  • 48
  • 75
0
for (UIView *object in _mapView.subviews) {
    if([[[object class] description] isEqualToString:@"GMSUISettingsPaddingView"] )
    {
        for (UIView *settingView in object.subviews) {
            if([[[settingView class] description] isEqualToString:@"GMSUISettingsView"] )
            {
                for(UIView *view in settingView.subviews) {
                    if([[[view class] description] isEqualToString:@"GMSx_QTMButton"] ) {
                        CGRect frame = view.frame;
                        frame.origin.y -= 60;
                        view.frame = frame;
                    }
                }
            }
        };

    }
}
Vikash Rajput
  • 445
  • 5
  • 21