I want to create multiple annotations with different images. To create the annotations, in viewDidLoad:
for (int i=0; i<[annotationsArray count]; i++) {
RMAnnotation *annotation = [[RMAnnotation alloc]
initWithMapView:mapView
coordinate:CLLocationCoordinate2DMake([[latitudeArray objectAtIndex:i] doubleValue], [[longitudeArray objectAtIndex:i] doubleValue])
andTitle:[nameArray objectAtIndex:i]];
[self.mapView addAnnotation:annotation];
}
In the method below, it gives every annotation the same image:
- (RMMapLayer *)mapView:(RMMapView *)mapView layerForAnnotation:(RMAnnotation *)annotation
{
if (annotation.isUserLocationAnnotation) {
return nil;
}
RMMarker *marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:#"CoffeeShopsIcon.png"]];
marker.canShowCallout = YES;
marker.leftCalloutAccessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"CoffeeShopsIcon.png"]];
marker.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return marker;
}
I have an array that holds the image names. How can I achieve this issue?
You want to use the -[RMAnnotation userInfo] API.
/** Storage for arbitrary data. */
#property (nonatomic, strong) id userInfo;
You could setup a property on your RMAnnotation that denotes which image to use and then create your marker based on this property for each annotation.
Related
i have many annotations on my map, How can i grouping Annotation Pins on the Same Coordinate?, i found this but i down't know how can i use that
I think do you mean use something like a cluster on your map for grouping the pins, isn't it?
I used sometime REVClusterMap:
https://github.com/RVLVR/REVClusterMap
Is very useful to avoid to show many points on the map. With that, you show groups of annotations (the clusters) when you are far on the map, and when you zoom in, the points are appearing.
You only have to define your map like this:
REVClusterMapView *mapView;
mapView = [[REVClusterMapView alloc] initWithFrame:viewBounds];
And when you have to add and define the annotations, it will be like this:
NSMutableArray *pins = [NSMutableArray array];
for(int i=0;i<50;i++) {
...
CLLocationCoordinate2D newCoord = {lat+latDelta, lng+lonDelta};
REVClusterPin *pin = [[REVClusterPin alloc] init];
pin.title = [NSString stringWithFormat:#"Pin %i",i+1];;
pin.subtitle = [NSString stringWithFormat:#"Pin %i subtitle",i+1];
pin.coordinate = newCoord;
[pins addObject:pin];
[pin release];
}
[mapView addAnnotations:pins];
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation {
...
REVClusterPin *pin = (REVClusterPin *)annotation;
MKAnnotationView *annView;
if( [pin nodeCount] > 0 ) { //cluster
pin.title = #"___";
annView = (REVClusterAnnotationView*)
[mapView dequeueReusableAnnotationViewWithIdentifier:#"cluster"];
if( !annView )
annView = (REVClusterAnnotationView*)
[[REVClusterAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"cluster"];
annView.image = [UIImage imageNamed:#"cluster.png"];
[(REVClusterAnnotationView*)annView setClusterText:
[NSString stringWithFormat:#"%i",[pin nodeCount]]];
annView.canShowCallout = NO;
}
else { //show your code for a single annotation
...
}
I hope it will be useful :)
Have your ADClusterMapView? It works really well for us.
I have an application which shows something around 100 Annotations (with custom pin-images, callout accessories and annotation-images) in a MapView. While building the annotations I store a link between annotation and building so I can assign the right building and open the right segue afterwards.
In iOS 6 they get built really fast, I also enabled animation while adding them, so one pin got dropped after the other, but with apple maps in iOS7 this isn't possible anymore (?). Now building those 100 annotations takes over 1 second on my iPhone 4S and that's too long. Is there anyway to improve the code?
- (void)viewDidLoad
...
//creating annotations
annotationlink = [[NSMutableArray alloc] init];
for (int i = 0; i < data.count; i++) {
NSDictionary *dataItem = [data objectAtIndex:i];
//storing annotation in array for link
Annotation *buildingannotation = [[Annotation alloc] init];
NSNumber *index = [NSNumber numberWithInteger:i];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:index, indexKey, buildingannotation, annotationKey, nil];
[annotationlink addObject:dict];
buildingannotation.title = [dataItem objectForKey:#"Building"];
buildingannotation.subtitle = [dataItem objectForKey:#"Info"];
MKCoordinateRegion buildingcoordinates;
buildingcoordinates.center.latitude = [[dataItem objectForKey:#"Latitude"] floatValue];
buildingcoordinates.center.longitude = [[dataItem objectForKey:#"Longitude"] floatValue];
buildingannotation.coordinate = buildingcoordinates.center;
[self.mapView addAnnotation:buildingannotation];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
MKAnnotationView *pinView = (MKAnnotationView *)
[self.mapView dequeueReusableAnnotationViewWithIdentifier:pinIdentifier];
MKAnnotationView *customAnnotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier];
customAnnotationView.canShowCallout = YES;
//right button to detail view
UIButton* disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
customAnnotationView.rightCalloutAccessoryView = disclosureButton;
//left button for image
NSInteger *buildingindex = [self getIndex:annotation];
NSDictionary *dataItem = [data objectAtIndex:buildingindex];
NSString* filename = [dataItem objectForKey:#"Thumb"];
filename = [filename stringByAppendingString:#"#2x.jpg"];
NSString* resourceimagePath = [resourcePath stringByAppendingPathComponent:filename];
Image = [UIImage imageWithContentsOfFile:resourceimagePath];
UIImageView *AnnotationThumb = [[UIImageView alloc] initWithImage:Image];
AnnotationThumb.frame = CGRectMake(0, 0, 31, 31);
customAnnotationView.leftCalloutAccessoryView = AnnotationThumb;
//annotation image
customAnnotationView.image = [UIImage imageNamed:#"Annotation_white.png"];
return customAnnotationView;
return pinView;
}
the following function gets the index of the current annotation using nspredicate to filter the array with the dictionaries. the advantage of this is the fact, that i can also use it when calloutAccessoryControlTapped:
-(NSInteger*) getIndex:(Annotation*)searchannotation
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#", annotationKey, searchannotation];
NSArray *filteredarray = [annotationlink filteredArrayUsingPredicate:predicate];
NSDictionary *building = [filteredarray objectAtIndex:0];
NSInteger *buildingIndex = [[building objectForKey:indexKey] integerValue];
return buildingIndex;
}
With an iPhone 4S the last pin is built 1.14 seconds after the view gets loaded.
if i search the annotation link array manually instead of using nspredicate function like this:
//left button for image
int buildingIndex;
for (int i = 0; i < annotationlink.count; i++) {
NSDictionary *annotationDict = [annotationlink objectAtIndex:i];
if ([[annotationDict objectForKey:annotationKey] isEqual:annotation]) {
buildingIndex= [[annotationDict objectForKey:indexKey] integerValue];
i = annotationlink.count;
}
}
NSDictionary *dataItem = [data objectAtIndex:buildingIndex];
the log says that the last pin is built 1.89 seconds after the viewDidLoad.
if i create the annotations in viewDidApper instead of viewDidLoad the View is shown off course immediately but the background takes some time to load so until the pins are dropped everything is gray which is also not very nice...
Thank you Anna for your suggestions! I implemented the improvements like this:
Annotation.h:
#import <MapKit/MKAnnotation.h>
#interface Annotation : NSObject <MKAnnotation> {}
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
#property(nonatomic, copy) NSString *title;
#property(nonatomic, copy) NSString *subtitle;
#property NSInteger *ID;
#end
Annotation.m:
#import "Annotation.h"
#implementation Annotation
#synthesize coordinate, title, subtitle, ID;
#end
ViewDidAppear:
//creating annotations
for (int i = 0; i < data.count; i++) {
NSDictionary *dataItem = [data objectAtIndex:i];
Annotation *buildingannotation = [[Annotation alloc] init];
buildingannotation.ID = i;
buildingannotation.title = [dataItem objectForKey:#"Building"];
buildingannotation.subtitle = [dataItem objectForKey:#"Subtitle"];
MKCoordinateRegion buildingcoordinates;
buildingcoordinates.center.latitude = [[dataItem objectForKey:#"Latitude"] floatValue];
buildingcoordinates.center.longitude = [[dataItem objectForKey:#"Longitude"] floatValue];
buildingannotation.coordinate = buildingcoordinates.center;
[self.mapView addAnnotation:buildingannotation];
}
viewForAnnotation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
MKAnnotationView *pinView = (MKAnnotationView *)
[self.mapView dequeueReusableAnnotationViewWithIdentifier:pinIdentifier];
if (pinView == nil) {
MKAnnotationView *customAnnotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier];
customAnnotationView.canShowCallout = YES;
//right button to detail view
UIButton* disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
customAnnotationView.rightCalloutAccessoryView = disclosureButton;
//left button for image
Annotation *buildingAnnotation = (Annotation *)annotation;
NSInteger *buildingindex = buildingAnnotation.ID;
NSString *filePath = [thumbname objectAtIndex:buildingindex];
UIImageView *AnnotationThumb = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:filePath]];
AnnotationThumb.frame = CGRectMake(0, 0, 31, 31);
customAnnotationView.leftCalloutAccessoryView = AnnotationThumb;
//annotation image
customAnnotationView.image = [UIImage imageNamed:#"Annotation_white.png"];
return customAnnotationView;
} else {
pinView.annotation = annotation;
}
return pinView;
}
Callout:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
Annotation *buildingAnnotation = (Annotation *)view.annotation;
selectedbuilding = buildingAnnotation.ID;
[self performSegueWithIdentifier:#"DetailViewController" sender:self];
}
Takes still some time for showing all Annotations. Is there any chance to further improve the code?
I updated the vievForAnnotation function regarding to Anna's reply and the PhotosByLocation Sample Application. It works now and I hope it's the correct way to implement the reuse...
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
MKAnnotationView *buildingAnnotationView = (MKAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:pinIdentifier];
if (buildingAnnotationView) {
[buildingAnnotationView prepareForReuse];
} else {
buildingAnnotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier];
buildingAnnotationView.canShowCallout = YES;
//right button to detail view
UIButton* disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
buildingAnnotationView.rightCalloutAccessoryView = disclosureButton;
//annotation image
buildingAnnotationView.image = [UIImage imageNamed:#"Annotation_white.png"];
}
//left button for image
Annotation *buildingAnnotation = (Annotation *)annotation;
NSInteger *buildingindex = buildingAnnotation.ID;
NSString *filePath = [thumbname objectAtIndex:buildingindex];
UIImageView *AnnotationThumb = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:filePath]];
AnnotationThumb.frame = CGRectMake(0, 0, 31, 31);
buildingAnnotationView.leftCalloutAccessoryView = AnnotationThumb;
return buildingAnnotationView;
}
I have a problem when trying to add a polyline on the map.
I get directions from MKDirections, and create a RMPolylineAnnotation, but it does not appear on the map.
As I can see, the following method is called just once when the map appears:
- (RMMapLayer *)mapView:(RMMapView *)mapView layerForAnnotation:(RMAnnotation *)annotation
Here is the code I am using:
- (void)getRoute {
CLLocation *source = [[CLLocation alloc] initWithLatitude:self.mapView.userLocation.coordinate.latitude longitude:self.mapView.userLocation.coordinate.longitude];
CLLocation *destination = [[CLLocation alloc] initWithLatitude:self.mapView.selectedAnnotation.coordinate.latitude longitude:self.mapView.selectedAnnotation.coordinate.longitude];
[AppleDirectionManager getDirectionsFromPoint:source toPoint:destination transportType:MKDirectionsTransportTypeAutomobile withCompletion:^(MKDirectionsResponse *result) {
self.routeStepsArray = [[result.routes firstObject] steps];
for (MKRoute *route in result.routes) {
NSMutableArray *points = [NSMutableArray array];
for (int i = 0; i < route.polyline.pointCount; i++) {
MKMapPoint point = route.polyline.points[i];
CLLocation *location = [[CLLocation alloc] initWithLatitude:point.x longitude:point.y];
[points addObject:location];
}
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *arr = [points copy];
RMPolylineAnnotation *polyline = [[RMPolylineAnnotation alloc] initWithMapView:self.mapView points:arr];
[self.mapView addAnnotation:polyline];
});
}
}];
}
- (RMMapLayer *)mapView:(RMMapView *)mapView layerForAnnotation:(RMAnnotation *)annotation {
if (annotation.isUserLocationAnnotation)
return nil;
RMMarker *marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:#"blabla.png"]];
[marker setCanShowCallout:YES];
[marker setName:#"blabla"];
return marker;
}
Also I can not show a custom callout for a RMPointAnnotation.
For RMPointAnnotation it's easily.
_polylineAnnotation = [[RMPolylineAnnotation alloc] initWithMapView:_mapView points:#[Location,Location]];
_polylineAnnotation.lineColor = [UIColor redColor];
_polylineAnnotation.lineWidth = 10.0f;
[_mapView addAnnotation:_polylineAnnotation];
I have the following files:
annotation.h:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface annotation : NSObject <MKAnnotation>
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
#property(nonatomic, copy) NSString *title;
#property(nonatomic, copy) NSString *subtitle;
#end
annotation.m:
#import "annotation.h"
#implementation annotation
#synthesize coordinate, title, subtitle;
#end
And in the main code, which takes in an NSURL found elsewhere:
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[spinner stopAnimating];
// json parsing
results = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
NSMutableArray * locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
annotation * myAnn;
NSArray *pins = mapView.annotations;
if ([pins count])
{
[mapView removeAnnotations:pins];
}
/* loop through the array, each key in the array has a JSON String with format:
* title <- string
* strap <- string
* id <- int
* date <- date
* lat <- floating point double
* long <- floating point double
* link <- string
*/
int i;
for (i = 0; i < [results count]; i++) {
//NSLog(#"Result: %i = %#", i, results[i]);
//NSLog(#"%#",[[results objectAtIndex:i] objectForKey:#"long"]);
myAnn = [[annotation alloc] init];
location.latitude = (double)[[[results objectAtIndex:i] objectForKey:#"lat"] doubleValue];
location.longitude = (double)[[[results objectAtIndex:i] objectForKey:#"long"] doubleValue];
myAnn.coordinate = location;
myAnn.title = [[results objectAtIndex:i] objectForKey:#"title"];
myAnn.subtitle = [[results objectAtIndex:i] objectForKey:#"strap"];
[locations addObject:myAnn];
//NSLog(#"%i", [[results objectAtIndex:i] objectForKey:#"lat"]);
}
[self.mapView addAnnotations:locations];
Previous things I have looked at for this say that I need to use MKAnnotationView as opposed to MKPinAnnotationView but as you can see I do not use either, is it possible for me to use custom images for the pins that are dropped on the screen.
You (a) make sure to define your view controller to be the delegate for your MKMapView; and (b) implement viewForAnnotation, e.g.:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
if ([annotation isKindOfClass:[CustomAnnotation class]]) {
static NSString * const identifier = #"MyCustomAnnotation";
MKAnnotationView* annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView) {
annotationView.annotation = annotation;
} else {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:identifier];
}
// set your annotationView properties
annotationView.image = [UIImage imageNamed:#"Your image here"];
annotationView.canShowCallout = YES;
// if you add QuartzCore to your project, you can set shadows for your image, too
//
// [annotationView.layer setShadowColor:[UIColor blackColor].CGColor];
// [annotationView.layer setShadowOpacity:1.0f];
// [annotationView.layer setShadowRadius:5.0f];
// [annotationView.layer setShadowOffset:CGSizeMake(0, 0)];
// [annotationView setBackgroundColor:[UIColor whiteColor]];
return annotationView;
}
return nil;
}
By the way, in my example above, I changed the name of your annotation class to CustomAnnotation. annotation is a horrible name for a class because (a) it doesn't follow class naming conventions of upper case first letter; and (b) it's identical to the variable name, annotation, that many MKMapViewDelegate methods will use by default.
References
Location Awareness Programming Guide
Map Kit Framework Reference
You can certainly use custom images for an MKAnnotationView, see iOS MapKit custom pins.
Both MKAnnotationPinView and MKAnnotationView are subclasses of UIView and therefore will allow custom views.
I added a plist database to store information for annotations in a MKMapView. Once I implemented the code to grab the information, my delegate methods are no longer being called.
The code I added was:
- (void)viewDidLoad
{
NSMutableArray *annotations = [[NSMutableArray alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"MillersStores" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *anns = [dict objectForKey:#"Root"];
for(int i = 0; i < [anns count]; i++) {
float realLatitude = [[[anns objectAtIndex:i] objectForKey:#"Latitude"] floatValue];
float realLongitude = [[[anns objectAtIndex:i] objectForKey:#"Longitude"] floatValue];
MillersLocations *myAnnotation = [[MillersLocations alloc] init];
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = realLatitude;
theCoordinate.longitude = realLongitude;
myAnnotation.coordinate = theCoordinate;
myAnnotation.title = [[anns objectAtIndex:i] objectForKey:#"Title"];
myAnnotation.subtitle = [[anns objectAtIndex:i] objectForKey:#"Address"];
[mapView addAnnotation:myAnnotation];
[annotations addObject:myAnnotation];
[myAnnotation release];
}
}
And this is one of delegate method that's no longer being called is:
- (MKAnnotationView *) mapView:(MKMapView *) mapView viewForAnnotation:(id ) annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
MKPinAnnotationView *pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil] autorelease];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
UIImageView *leftIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"MillersAnnotation.png"]];
pinView.leftCalloutAccessoryView = leftIconView;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
I have another delegate method in there that also zooms into the User's Location that's not being called either, as well as the calloutAccessoryControlTapped method that's no longer being called.
I know it has something to do with the new code, but I'm confused as to how to even debug this because it's not giving me errors and I can't log it because the entire methods aren't even being called. When I get rid of the new code, the old code works fine...What is it in the new code that negates the old code?
It sounds like the map view's delegate property is not set.
Did the old code contain this line:
mapView.delegate = self;
Add that to viewDidLoad or, in IB, connect the map view's delegate outlet to File's Owner.