Question

I am using Google Maps in iOS Application and have implemented a custom info window for showing title of a marker.
Now, I added a button on that custom info window but my problem is that the button action method does not get called.

CustomInfoWindow.h

#import <UIKit/UIKit.h>

@interface CustomInfoWindow : UIView
@property (nonatomic,weak) IBOutlet UILabel *addressLabel;
@property(nonatomic) IBOutlet UIButton *button;
@end

and in infoWindow.xib, I have added

  • UILabel called addressLabel
  • UIButton called button

ViewController.h

#import "CustomInfoWindow.h"

@interface viewController : UIViewController<GMSMapViewDelegate>
{
  GMSMapView *mapView;
}
@end

ViewController.m

- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker
{
    NSLog(@"Mrker Tapped");

    CustomInfoWindow *infoWindow = [[[NSBundle mainBundle]loadNibNamed:@"infoWindow"
                                                                 owner:self
                                                               options:nil] objectAtIndex:0];
    infoWindow.addressLabel.text = marker.title;    

    [infoWindow.button addTarget:self action:@selector(ButtonPressed) 
                            forControlEvents:UIControlEventTouchUpInside];
    return infoWindow;
}

-(void)ButtonPressed
{
    NSLog(@"Button Pressed");
}

Basically... ButtonPressed method does not fire.

Was it helpful?

Solution

I haven't used Google Maps SDK but after following a few links, it seems what you're trying to accomplish may not be easy.

ref: https://developers.google.com/maps/documentation/android/infowindows
PS: This maybe Android documentation but it seems it holds true for iOS as well.

To quote:

Note: The info window that is drawn is not a live view. The view is rendered as an image (using View.draw(Canvas)) at the time it is returned. This means that any subsequent changes to the view will not be reflected by the info window on the map. To update the info window later (for example, after an image has loaded), call showInfoWindow(). Furthermore, the info window will not respect any of the interactivity typical for a normal view such as touch or gesture events. However you can listen to a generic click event on the whole info window as described in the section below.

You could instead implement GMSMapViewDelegate's -didTapInfoWindowOfMarker: delegate method to check if the infowindow was tapped.
(the drawback is that the entire infowindow becomes one button)


Other links:

similar question 1
similar question 2

OTHER TIPS

Look at this code of yours -

[infoWindow.button addTarget:self action:@selector(ButtonPressed) forControlEvents:UIControlEventTouchUpInside];

in this I guess your button name should start with capital "B" as:-

[infoWindow.Button addTarget:self action:@selector(ButtonPressed) forControlEvents:UIControlEventTouchUpInside];

since you have @property(nonatomic) IBOutlet UIButton *Button;

  • First write CustomInfoWindow *infoWindow it's property in .h header file.

  • Then synthesise in .m class file and then try to add the target

I think the infoWindow is too small and buttons on it placed outside it.

Try with checking the frame of infoWindow. Also you can apply any background color to infoWindow. Or do infoWindow.clipToBounds=YES. If button is placed out side the view then it defiantly get clipped.

Change this

[infoWindow.button addTarget:self action:@selector(ButtonPressed) forControlEvents:UIControlEventTouchUpInside];

to

[infoWindow.button addTarget:infoWindow action:@selector(ButtonPressed) forControlEvents:UIControlEventTouchUpInside];

And declare ButtonPressed in CustomInfoWindow class.

Swift 3.0 Solution

First set the delegate of mapView (GMSMapViewDelegate)

 //empty the default infowindow
        func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
            return UIView()
        }

        // reset custom infowindow whenever marker is tapped
        func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {

            customInfoView.removeFromSuperview()
        //    customInfoView.button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
            self.view.addSubview(customInfoView)

            // Remember to return false
            // so marker event is still handled by delegate
            return false
        }

        // let the custom infowindow follows the camera
        func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
            if (locationMarker != nil){
                let location = locationMarker.position
                customInfoView.center = mapView.projection.point(for: location)
            }
        }

        // take care of the close event
        func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
            customInfoView.removeFromSuperview()
        }

I have added customInfoWindow like this

and make outlet of this view(customInfoWindow) in same controller which has mapView.

I got the idea from this link thanks to this developer Custom and interactive googlemaps(IOS SDK) infowindow

You can't add a button inside of a infoWindow.

To do that, you have to create your own custom info window, and add the button in it.

map_ViewController.h

#import <UIKit/UIKit.h>
#import <GoogleMaps/GoogleMaps.h>

#import "CustomInfoWindow.h"

@interface Map_ViewController : UIViewController <GMSMapViewDelegate>{
    BOOL canHideInfoWindow;
}
@property (strong, nonatomic) IBOutlet GMSMapView *map;
@property (strong, nonatomic) CustomInfoWindow *displayedInfoWindow;
@property BOOL markerTapped;
@property BOOL cameraMoving;
@property BOOL idleAfterMovement;
@property (strong, nonatomic) GMSMarker *currentlyTappedMarker;
@property (assign, nonatomic) CLLocationCoordinate2D position;


@end

Map_ViewController.m

#import "Map_ViewController.h"

@interface Map_ViewController ()
@end
@implementation Map_ViewController



#pragma mark - viewDidLoad
- (void)viewDidLoad {
    [super viewDidLoad];

    //get user position, i wont put all the code, only the code necessary for this topic
     self.position =CLLocationCoordinate2DMake(POSITION);

    GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:self.position.latitude longitude:self.position.longitude zoom:15 bearing:0 viewingAngle:60];

    _map = [GMSMapView mapWithFrame:CGRectZero camera:camera];
    self.view = _map;
    [self createMarkers]
}






#pragma mark  create markers
-(void)createMarkers{
    //settings(controls) on map
    _map.delegate = self;
    self.edgesForExtendedLayout = UIRectEdgeNone;
    _map.accessibilityElementsHidden = NO;
    _map.settings.compassButton      = YES;
    _map.settings.myLocationButton   = YES;
    _map.myLocationEnabled           = YES;
    [_map setMinZoom:8 maxZoom:19 ];

    //remove all markers before to create the new one
    [_map clear];

    //this code above will center all markers on the map 
    GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] init];
    //creates pin markers

    //for this code, i used an model to store all markers.. again, i wont write it here.. so create by yourself the loop for it
    for (marker in markersArray){
        GMSMarker *marker = [[GMSMarker alloc] init];

        marker.position = CLLocationCoordinate2DMake(LAtLongPosition);

        marker.appearAnimation = kGMSMarkerAnimationPop;
        marker.userData = marker;
        marker.map = _map;
        bounds = [bounds includingCoordinate:marker.position];
    }

    //include user location too
    bounds = [bounds includingCoordinate:self.position];


    //center the map
    [_map animateWithCameraUpdate:[GMSCameraUpdate fitBounds:bounds withPadding:50]];
}











#pragma mark - GoogleMaps Delegate
// Since we want to display our custom info window when a marker is tapped, use this delegate method
- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker{

    // A marker has been tapped, so set that state flag
    self.markerTapped = YES;

    // If a marker has previously been tapped and stored in currentlyTappedMarker, then nil it out
    if(self.currentlyTappedMarker) {
        self.currentlyTappedMarker = nil;
    }

    // make this marker our currently tapped marker
    self.currentlyTappedMarker = marker;

    // if our custom info window is already being displayed, remove it and nil the object out
    if([self.displayedInfoWindow isDescendantOfView:self.view]) {
        [self.displayedInfoWindow removeFromSuperview];
        self.displayedInfoWindow = nil;
    }

    /* animate the camera to center on the currently tapped marker, which causes
     mapView:didChangeCameraPosition: to be called */
    GMSCameraUpdate *cameraUpdate = [GMSCameraUpdate setTarget:marker.position];
    [_map animateWithCameraUpdate:cameraUpdate];

    return YES;
}





- (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position{
    cameraMoving state flag to YES
    if(self.markerTapped) {
        self.cameraMoving = YES;
    }

    //Move the custom info window with the map
    CGPoint markerPoint = [_map.projection pointForCoordinate:self.currentlyTappedMarker.position];
    CGRect frame = self.displayedInfoWindow.bounds;
    frame.origin.y = markerPoint.y - self.displayedInfoWindow.frame.size.height - 15 ;
    frame.origin.x = markerPoint.x - self.displayedInfoWindow.frame.size.width / 2;
    self.displayedInfoWindow.frame = frame;
}






/* If the map is tapped on any non-marker coordinate, reset the currentlyTappedMarker and remove our
 custom info window from self.view */
- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate{
    if(self.currentlyTappedMarker) {
        self.currentlyTappedMarker = nil;
    }

    if([self.displayedInfoWindow isDescendantOfView:self.view]) {
        [self.displayedInfoWindow removeFromSuperview];
        self.displayedInfoWindow = nil;
    }
}







#pragma mark create infoWindow
// This method gets called whenever the map was moving but has now stopped
- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position{
    /* if we got here and a marker was tapped and our animate method was called, then it means we're ready
     to show our custom info window */
    if(self.markerTapped && self.cameraMoving) {

        infosMarker = self.currentlyTappedMarker.userData;

        // reset our state first
        self.cameraMoving = NO;
        self.markerTapped = NO;
        self.idleAfterMovement = YES;


        //CREATE YOUR INFO WINDOW VIEW (CustomInfoWindow : UIView)and load it
        self.displayedInfoWindow = [[[NSBundle mainBundle] loadNibNamed:@"CustomInfoWindow" owner:self options:nil] objectAtIndex:0];
        CGPoint markerPoint = [_map.projection pointForCoordinate:self.currentlyTappedMarker.position];
        CGRect frame = self.displayedInfoWindow.bounds;
        frame.origin.y = markerPoint.y - self.displayedInfoWindow.frame.size.height - 15;
        frame.origin.x = markerPoint.x - self.displayedInfoWindow.frame.size.width / 2;
        self.displayedInfoWindow.frame = frame;





        [self.displayedInfoWindow.YOURBUTTON addTarget:self action:@selector(YOURBUTTONFUNCTION:) forControlEvents:UIControlEventTouchUpInside];


        [self.view addSubview:self.displayedInfoWindow];
    }
}




-(void)YOURBUTTONFUNCTION:(UIButton *)sender{
    NSLog(@"YOURBUTTONFUNCTION TAPPED");
}








@end
  1. Create one subview with button in it.
  2. Assign frame of infoWindow view to subview frame.

    [subView setFrame:infoview.frame];
    subView =  [[[NSBundle mainBundle] loadNibNamed:<YourViewName> owner:self options:nil] objectAtIndex:0]; [self.mapview addSubview:subView];
    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top