I know it's easy in the java version of Google Maps but I can't figure out how to get the info window to close in the objective C version of the SDK.
I'm using this method:
-(void) mapView:(GMSMapView *)mapView
didTapInfoWindowOfMarker:(id<GMSMarker>)marker {
sharedGlobal.shouldShowPlayer = YES;
/* adds the path to the map by decoding google's encoded string */
[self addPath: sharedGlobal.encodedPathString];
}
And want to add a line to close the infowindow associated with marker.
I think you can use this:
mapView.selectedMarker = nil;
The comments on the selectedMarker property in GMSMapView.h say this:
/**
* The marker that is selected. Setting this property selects a particular
* marker, showing an info window on it. If this property is non-nil, setting
* it to nil deselects the marker, hiding the info window. This property is
* observable using KVO.
*/
#property (nonatomic, strong) id<GMSMarker> selectedMarker;
SWIFT
mapView.selectedMarker?.title = nil
Related
I have an app that sends a notification every time the user approaches to a map on GMSMapView.
I want that when the user opens the notification (UILocalNotification) the marker will be already selected and its info window will be visible.
I've searched for a method in the delegate that do that (selectAMarker:/selectAMarkerInPosition: or something like that) but I haven't found something like that.
Anyone knows a method that do that/know how can I do that?
Is it even possible?
Thank you very much!
I think you like this method
-(BOOL) mapView:(GMSMapView *) mapView didTapMarker:(GMSMarker *)marker
{
NSLog(#"try");
return YES;
}
Dont Forget to call delegate
#interface ViewController : UIViewController <GMSMapViewDelegate>
and
yourmapView_.delegate = self;
I'm trying to code in a basic ArcGIS workflow into my iOS project. I am new to this platform and can use some pointers. My workflow is as follows.
1.) Create mapView displaying satellite (world style) map.
2.) Add a public webMap from my user account on ArcGIS Online as an overlay/layer on the satellite map.
What I've tried
//.h
#import <ArcGIS/ArcGIS.h>
#interface ViewController : UIViewController <AGSWebMapDelegate>
#property (strong, nonatomic) IBOutlet AGSMapView *mapView;
//.m
// Add basemap
NSURL* url = [NSURL URLWithString:#"http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"];
AGSTiledMapServiceLayer *tiledLayer = [AGSTiledMapServiceLayer tiledMapServiceLayerWithURL:url];
[self.mapView addMapLayer:tiledLayer withName:#"Basemap Tiled Layer"];
//If I run this part alone, I'll get the satellite map.
AGSWebMap* webmap = [[AGSWebMap alloc] initWithItemId:#"bb9b8c172e8142f995526bf658078f54" credential:nil];
webmap.delegate = self;
[webmap openIntoMapView:self.mapView];
//When I add this webMap code and run the project, I get a blank white screen.
//.m cont.
- (void)mapViewDidLoad:(AGSMapView *) mapView {
NSLog(#"mapView didLoad");
}
- (void) webMapDidLoad:(AGSWebMap*) webMap {
NSLog(#"webmap added successfully");
}
//Neither of these logs get called.
Questions.
1.) Is it right to use AGSTiledMapServiceLayer as my basemap? Also, is it right to use AGSWebMap for my ArcGIS Online map?
2.) My goal is to be able to add and remove multiple layers to and from a satellite basemap, one at a time. Am I on the right track?
I'm currently using MapBox to achieve this but I'm starting to experiment with ArcGIS SDK and it's features.
Thanks in advance.
You are almost there. Make sure all 3 of these items are in the proper pace.
From the discussion from https://developers.arcgis.com/ios/objective-c/guide/viewing-web-map.htm.
Make these calls where you made them before (presumably viewDidLoad)
instantiate an AGSWebMap object // 1
An instance of your class must then be set as the web map's delegate // 2
// 1
AGSWebMap* webmap = [[AGSWebMap alloc] initWithItemId:#"bb9b8c172e8142f995526bf658078f54" credential:nil];
// 2
webmap.delegate = self;
Then update the handler webMapDidLoad() // 3
// open the web map into a map view in the delegate handler
- (void) webMapDidLoad:(AGSWebMap*) webMap {
// 3
[webmap openIntoMapView:self.mapView];
NSLog(#"webmap added successfully");
}
I am working on google map iOS sdk. Here I create multiple markers in separate location co-ordinates.
Now I need to add identifier such like TAG for all markers to perform action for particular marker.
If TAG or some other identifier option is not available in google map iOS sdk, please suggest me how to archive it.
Thanks in Advance.
What I do is that I simply inherit the GMSMarker and add whatever data I need to it like this, I guess this is the best and easiest option you have.
#interface ATGoogleMapsSelectiveMarker : GMSMarker
#property (nonatomic) int markerID;
#property (nonatomic) int order;
#property (strong, nonatomic) NSObject* referenceObject;
#property (nonatomic) BOOL selected;
#end
EDIT:
I thought it is clear but I'll continue on how to get the data... When you create your markers and add them to the map, you create ATGoogleMapsSelectiveMarker and add it to the map after you fill it with everything you need, then you register any class you want as a delegate which implements GMSMapViewDelegate and you implement this method
- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker {
// Here you are sure that your marker object is of type ATGoogleMapsSelectiveMarker but it won't harm to double check
if ([marker isKindOfClass:[ATGoogleMapsSelectiveMarker class]]) {
ATGoogleMapsSelectiveMarker* parsedMarker = (ATGoogleMapsSelectiveMarker*)marker;
NSLog(#"%d", parsedMarker.markerId);
}
return YES;
}
Swift solution :
You can add identifier or any thing to a marker by setting a value for a key for the marker:
marker.setValue("20", forKey: "id")
and you can get it later using :
marker.value(forKey: "id")
Or
My way to do it is by create an extension for the GMSMarker and using the userData property to make the GMSMarker fits my need, for me I want to add each marker with instance of Branch class (map with markers for branches of some company) and this is the way that I did:
extension GMSMarker {
var branch: Branch {
set(branch) {
self.userData = branch
}
get {
return self.userData as! Branch
}
}
}
so when I set the marker property I set it like this:
marker.branch = someBranch
Isn't that clearer and more readable than marker.userData = someBranch ??
I'm learning to use MapKit in my fledgling iOS app. I'm using some of my model entities as annotations (added the <MKAnnotation> protocol to their header file). I also create custom MKAnnotationViews and set the draggable property to YES.
My model object has a location property, which is a CLLocation*. To conform to the <MKAnnotation> protocol, I added the following to that object:
- (CLLocationCoordinate2D) coordinate {
return self.location.coordinate;
}
- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {
CLLocation* newLocation = [[CLLocation alloc]
initWithCoordinate: newCoordinate
altitude: self.location.altitude
horizontalAccuracy: self.location.horizontalAccuracy
verticalAccuracy: self.location.verticalAccuracy
timestamp: nil];
self.location = newLocation;
}
- (NSString*) title {
return self.name;
}
- (NSString*) subtitle {
return self.serialID;
}
So, I have the 4 required methods. And they're pretty straightforward. When I read the apple docs on MKAnnotationView and the #draggable property, it says the following:
Setting this property to YES makes an annotation draggable by the user. If YES, the associated annotation object must also implement the setCoordinate: method. The default value of this property is NO.
And elsewhere, the MKAnnotation docs say:
Your implementation of this property must be key-value observing (KVO) compliant. For more information on how to implement support for KVO, see Key-Value Observing Programming Guide.
I have read that (brief) document, and it is not clear at all to me what I'm supposed to do to accomplish that so that the coordinate, which I'm deriving from my location property is a proper property in and of itself.
But I'm reasonably sure it's not working correctly. When I drag the pin, it moves, but then it no longer relocates when I pan the map.
UPDATE
So I tried playing with the stock MKPinAnnotationView. To do this, I simply commented out my delegate's mapView:viewForAnnotation: method. I discovered that these aren't draggable by default. I added the mapView:didAddAnnotationViews: to my delegate to set the draggable property of the added views to YES.
Once configured thus, the Pin views, as hinted by John Estropia below, seem to work fine. I decided to use the mapView:annotationView:didChangeDragState:fromOldState: delegate hook to get a closer look at what is going on:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState {
NSArray* states = #[#"None", #"Starting", #"Dragging", #"Cancelling", #"Ending"];
NSLog(#"dragStateChangeFrom: %# to: %#", states[oldState], states[newState]);
}
For the stock pins, one will see log output that looks like this:
2014-02-05 09:07:45.924 myValve[1781:60b] dragStateChangeFrom: None to: Starting
2014-02-05 09:07:46.249 myValve[1781:60b] dragStateChangeFrom: Starting to: Dragging
2014-02-05 09:07:47.601 myValve[1781:60b] dragStateChangeFrom: Dragging to: Ending
2014-02-05 09:07:48.006 myValve[1781:60b] dragStateChangeFrom: Ending to: None
Which looks pretty logical. But if you switch to the configured MKAnnotationView, the output you will see looks like:
2014-02-05 09:09:41.389 myValve[1791:60b] dragStateChangeFrom: None to: Starting
2014-02-05 09:09:45.451 myValve[1791:60b] dragStateChangeFrom: Starting to: Ending
It misses TWO transitions, from Starting to Dragging, and from Ending to None.
So I begin to be skeptical that I need to do something different with properties. But I'm still frustrated with why this won't work.
UPDATE 2
I created my own Annotation object to stand between my model objects, which could have a property coordinate property. The behavior remains the same. It seems to be something with the MKAnnotationView.
There are lots of examples about how to use the delegate method mapView:viewForAnnotation: that show setting up an MKAnnotationView. But what is not as obvious is that just because you set the draggable property of your MKAnnotationView instance to YES, you still have to write some code to help it transition some of the states. MapKit will take care of moving your instance's dragState to MKAnnotationViewDragStateStarting and MKAnnotationViewDragStateEnding, but it will not do the other transitions. You see hints of this in the docs notes about subclassing MKAnnotationView and the need to override the `setDragState:animated:'
When the drag state changes to MKAnnotationViewDragStateStarting, set the state to MKAnnotationViewDragStateDragging. If you perform an animation to indicate the beginning of a drag, and the animated parameter is YES, perform that animation before changing the state.
When the state changes to either MKAnnotationViewDragStateCanceling or MKAnnotationViewDragStateEnding, set the state to MKAnnotationViewDragStateNone. If you perform an animation at the end of a drag, and the animated parameter is YES, you should perform that animation before changing the state.
In this case, I'm not subclassing, but it seems that MKAnnotationView still struggles to make the transitions on its own. So you have to implement the delegate's mapView:annotationView:didChangeDragState:fromOldState: method. E.g.
- (void)mapView:(MKMapView *)mapView
annotationView:(MKAnnotationView *)annotationView
didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState {
if (newState == MKAnnotationViewDragStateStarting) {
annotationView.dragState = MKAnnotationViewDragStateDragging;
}
else if (newState == MKAnnotationViewDragStateEnding || newState == MKAnnotationViewDragStateCanceling) {
annotationView.dragState = MKAnnotationViewDragStateNone;}
}
}
This allows things to complete appropriately, so that when you pan the map after dragging the annotation, the annotation moves with the pan.
Are you using a custom map pin? I saw this before as well. Seems to be a bug in iOS 7. As a workaround, we just ended up using the default pin for MKPinAnnotationView.
Your setCoordinate was missing the required Key-Value observing methods (willChangeValueForKey/didChangeValueForKey) that are required for the map to detect when the annotation has been moved so it can move the annotation view to match, e.g.
- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {
[self willChangeValueForKey:#"coordinate"];
CLLocation* newLocation = [[CLLocation alloc]
initWithCoordinate: newCoordinate
altitude: self.location.altitude
horizontalAccuracy: self.location.horizontalAccuracy
verticalAccuracy: self.location.verticalAccuracy
timestamp: nil];
self.location = newLocation;
[self didChangeValueForKey:#"coordinate"];
}
Source:
https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKAnnotation_Protocol/index.html#//apple_ref/occ/intfp/MKAnnotation/coordinate
"Your implementation of this property must be key-value observing (KVO) compliant. For more information on how to implement support for KVO, see Key-Value Observing Programming Guide."
Sorry Apple wasn't clear in the docs, what they meant to say was the setCoordinate requires the Key-Value observing methods willChangeValueForKey & didChangeValueForKey that allow the map to detect when the annotation has been moved so it can move the annotation view to match, e.g.
- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {
[self willChangeValueForKey:#"coordinate"];
CLLocation* newLocation = [[CLLocation alloc]
initWithCoordinate: newCoordinate
altitude: self.location.altitude
horizontalAccuracy: self.location.horizontalAccuracy
verticalAccuracy: self.location.verticalAccuracy
timestamp: nil];
self.location = newLocation;
[self didChangeValueForKey:#"coordinate"];
}
EDITED QUESTION:
I'm trying to take annotations created in map view (with title/subtitle) and push all annotations on my map to a tableview showing the list with title/subtitle.
I have a RegionAnnotation.h/.m NSObject file that works the reverse geocoding I need to populate the pins on my MapViewController. This works just fine. I do a long press which create a pin and the title and subtitle show up and the reverse geocoding works.
Now I want to push the pin data to a tableview list. I have tried calling the region annotation information within the cellForRowAtIndexPath and I use UITableViewCellStyleSubtitle in order to get the correct format for the cell to populate the title and subtitle. However when I call the following:
if (cell == nil){
NSLog(#"if cell");
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
CLPlacemark *pins = [self.annotations objectAtIndex:indexPath.row];
RegionAnnotation *annotation = [[RegionAnnotation alloc] initWithLocationIdentifier:(NSString *)pins];
cell.textLabel.text = annotation.title;
I only get the title, which is this case is "Location Reminder", however, the subtitle information which is the address does not populate the table. All that shows is the text "subtitle".
How do I populate the cell with both title and subtile information. I've been working on this for over a month and can't seem to find a solution. Please help.
Some comments on the latest code posted:
CLPlacemark *pins = [self.annotations objectAtIndex:indexPath.row];
RegionAnnotation *annotation = [[RegionAnnotation alloc]
initWithLocationIdentifier:(NSString *)pins];
This doesn't look right. The pins variable is declared as a CLPlacemark (which is suspicious in itself because CLPlacemark doesn't conform to MKAnnotation so why is it in an "annotations" array?) and then it is being casted an NSString * which has no relationship to a CLPlacemark. The cast is not going convert pins into a string -- it's going to treat the data pointed to by pins as if it was a NSString (which it isn't).
There are too many other issues, questions and unknowns with the previous code that was posted as well.
Instead, I'll give an example of how you might pass the annotations that are on the map view to a table view and show the data in the cells...
In the PinListViewController (the one with the table view), we declare an NSArray property to receive and reference the annotations:
//in the .h:
//Don't bother declaring an ivar with the same name.
#property (nonatomic, retain) NSArray *annotations;
//in the .m:
#synthesize annotations;
Next, in the MapViewController, in the place where you want to present/push/show the PinListViewController, the code would be something like this:
PinListViewController *plvc = [[PinListViewController alloc] init...
//pass the map view's annotations array...
plvc.annotations = mapView.annotations;
[self presentModalViewController:plvc animated:YES]; //or push, etc.
[plvc release]; //remove if using ARC
An important point here is that this example sends the entire annotations array. If you are showing the user's current location using showsUserLocation = YES then the array will include that annotation as well. If you only want to send certain annotations, you'll have to first build a new array containing the ones you want from the map view array and set plvc.annotations equal to that new array. A simple way to do that is to loop through the mapView.annotations and if an annotation is one you want to include, add it to the new array using addObject. Another possible issue with using the map view's annotations array directly is that if the annotations on the map change (are added/removed) while the table view is still showing the annotations list, it will go out of sync and may cause run-time range exceptions. To avoid that, if necessary, you could set plvc.annotations to a copy of the map view's annotations array (ie. [mapView.annotations copy]).
In PinListViewController, in the numberOfRowsInSection method:
return self.annotations.count;
In PinListViewController, in the cellForRowAtIndexPath method:
//typical dequeue/alloc+init stuff here...
//assume cell style is set to UITableViewCellStyleSubtitle
id<MKAnnotation> annotation = [self.annotations objectAtIndex:indexPath.row];
cell.textLabel.text = annotation.title;
cell.detailTextLabel.text = annotation.subtitle;
return cell;
The annotation is declared as id<MKAnnotation> so it will work with any type of annotation since the example only needs to show the standard title and subtitle properties. If you needed to show custom properties you may have in your custom annotation class, you would use isKindOfClass to check if annotation is of that type and then you can cast it to that custom class and reference the custom properties.