I want to add a number of annotations(arround 500) on a mapview but the maximum it seems to display is 100. If I go beyond 100, viewForAnnotation delegate method is not called. However it works perfectly for annotations below 100.
here is the code: (works only when count of annotationArray array is less than 101)
_allPoints = [[NSMutableArray alloc] init];
NSString* responseFile = [[NSBundle mainBundle] pathForResource:#"Datafile" ofType:#"txt"];
NSData *sampleData = [NSData dataWithContentsOfFile:responseFile];
if (sampleData) {
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:sampleData
options:kNilOptions
error:&error];
NSArray* response = [json objectForKey:#"response"];
for (NSDictionary *lineDict in response) {
if ([[lineDict objectForKey:#"data"] isKindOfClass:[NSArray class]]) {
SinglePoint *singlePoint = [[SinglePoint alloc] initWithDictionary:lineDict];
[_allPoints addObject:singlePoint];
}
else {
NSLog(#"Error");
}
}
}
_mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[_mapView setDelegate:self];
[self.view addSubview:_mapView];
NSMutableArray *annotationArray = [[NSMutableArray alloc] init];
for (int i=0; i<[_allPoints count]; i++) {
SinglePoint *singlePoint = [_allPoints objectAtIndex:i];
NVPolylineAnnotation *annotation = [[NVPolylineAnnotation alloc] initWithPoint:singlePoint mapView:_mapView];
[annotationArray addObject:annotation];
}
[_mapView addAnnotations:(NSArray *)annotationArray];
CLLocationCoordinate2D centerCoord = { 28.632747, 77.219982 };
[_mapView setCenterCoordinate:centerCoord zoomLevel:12 animated:NO];
The delegate method is:
EDIT: As per comments, started reusing the view but with no luck :(
if ([annotation isKindOfClass:[NVPolylineAnnotation class]]) {
static NSString *viewIdentifier = #"annotationView";
NVPolylineAnnotationView *annotationView = (NVPolylineAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:viewIdentifier];
if (annotationView == nil) {
annotationView = [[NVPolylineAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:viewIdentifier mapView:_mapView];
}
return annotationView;
}
return nil;
I could not find any restriction in the documentation or anywhere else. Could it be memory issue?
The MKMapView has not Limit for annotationViews. But it gets quite laggy and unusable above a certain number of views (+1000).
I believe, that the reason for this is that you handle the annotationView management totally wrong. You shouldn't create a unique view for every single annotation, even if you are using ARC. Your rather should reuse every unused View like the cell of a UITableView.
There are a couple of good tutorials for this like Introduction to MapKit on iOS - Ray Wenderlich.
If this won't resolve your problem, you should try to debug your annotation classes. (NVPolylineAnnotation and NVPolylineAnnotationView). Maybe there's something wrong.
Did you also checked your points array for equal coordinates?
Finally was able to track down the problem. Solved it by setting the center of mapView first and then adding annotations later. I still dont know why 100 annotations were shown earlier (and why the no 100 only). Thank you all for your suggestions and time on this.
This is the code I changed:-
_mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[_mapView setDelegate:self];
[self.view addSubview:_mapView];
CLLocationCoordinate2D centerCoord = { 28.632747, 77.219982 };
[_mapView setCenterCoordinate:centerCoord zoomLevel:12 animated:NO];
NSMutableArray *annotationArray = [[NSMutableArray alloc] init];
for (int i=0; i<[_allPoints count]; i++) {
SinglePoint *singlePoint = [_allPoints objectAtIndex:i];
NVPolylineAnnotation *annotation = [[NVPolylineAnnotation alloc] initWithPoint:singlePoint mapView:_mapView];
[annotationArray addObject:annotation];
}
[_mapView addAnnotations:(NSArray *)annotationArray];
You can add as many annotations as you like. However, the views for each annotation will not be created until that annotation's coordinate property intersects with the visible portion of your map view. MapKit will not create a view just because you added an annotation to the map.
Related
Basically I need a way to put annotation on all the "Walmarts" that search request picks up. I am not using interface builder, I am just using code for this app.
MKMapView * map = [[MKMapView alloc] initWithFrame:
CGRectMake(0, 0, 320, 480)];
map.delegate = self;
[self.view addSubview:map];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = #"Walmart";
request.region = map.region;
I'd suggest simply creating the mapview in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
MKMapView * map = [[MKMapView alloc] initWithFrame:self.view.bounds];
map.delegate = self;
[self.view addSubview:map];
}
But when the user moves the map, look for Walmarts and add the annotations:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
// if on slow network, it's useful to keep track of the previous
// search, and cancel it if it still exists
[self.previousSearch cancel];
// create new search request
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = #"Walmart";
request.region = mapView.region;
// initiate new search
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
NSMutableArray *annotations = [NSMutableArray array];
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop) {
// if we already have an annotation for this MKMapItem,
// just return because you don't have to add it again
for (id<MKAnnotation>annotation in mapView.annotations)
{
if (annotation.coordinate.latitude == item.placemark.coordinate.latitude &&
annotation.coordinate.longitude == item.placemark.coordinate.longitude)
{
return;
}
}
// otherwise, add it to our list of new annotations
// ideally, I'd suggest a custom annotation or MKPinAnnotation, but I want to keep this example simple
[annotations addObject:item.placemark];
}];
[mapView addAnnotations:annotations];
}];
// update our "previous" search, so we can cancel it if necessary
self.previousSearch = localSearch;
}
Clearly, this code sample assumes that you have a weak property for the previous search operation. (This is not, strictly speaking, necessary, but may give you better performance if browsing around on a map when you have a slow Internet connection.) Anyway, that property would be defined as follows:
#property (nonatomic, weak) MKLocalSearch *previousSearch;
There are other possible refinements (e.g. UIActivityIndicatorView or network activity indicator if search is in progress, perhaps remove annotations that are not within the map's current region, etc.), but hopefully you get the idea.
Here is the approach:
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[self.localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
if(error)
{
NSLog(#"localSearch startWithCompletionHandlerFailed! Error: %#", error);
return;
}
else
{
for(MKMapItem *mapItem in response.mapItems)
{
MKPinAnnotation *annotation = [[MKPinAnnotation alloc] initWithCoordinate: mapItem.placemark.location.coordinate];
[map addAnnotation:annotation];
NSLog(#"Name for result: = %#", mapItem.name);
}
}}];
My app is real-time tracker, where multiple users are logged in and updating their location by sending their co-ordinates to our web service which is then called back let's after every 2 minutes to show all the users on my MapView.
Every time I get the locations of users from web service in connectionDidFinishLoading method, I am parsing, creating polyline through pointsArray and adding them to overlay:
-(void) connectionDidFinishLoading: (NSURLConnection *) connection
{
userLatitudeArray = [[NSMutableArray alloc]init];
userLongitudeArray = [[NSMutableArray alloc]init];
userIdArray = [[NSMutableArray alloc]init];
userNameArray = [[NSMutableArray alloc]init];
userProfilePicArray = [[NSMutableArray alloc]init];
profilePicURLStringArray = [[NSMutableArray alloc]init];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSArray *trackingDict = [NSJSONSerialization JSONObjectWithData:empJsonData options:kNilOptions error:nil];
if ([trackingDict count] >= 2) {
for (trackUsersCount = 0; trackUsersCount< trackingDict.count; trackUsersCount++) {
NSLog(#"trackUsersCount %i", trackUsersCount);
NSMutableArray *latlongArray = [[NSMutableArray alloc]init];
latlongArray = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"latlong"];
[userLongitudeArray removeAllObjects];
[userLatitudeArray removeAllObjects];
for (int i = 0; i<latlongArray.count; i++) {
[userLatitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"lat"]];
[userLongitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"long"]];
}
NSString *name = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_firstName"];
// ProfilePIC URL
profilePicURLString = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_profilePicture"];
[userNameArray addObject:name];
[profilePicURLStringArray addObject:profilePicURLString];
int i;
if (userLatitudeArray.count>1) {
for (i = 0; i<userLatitudeArray.count; i++) {
CLLocationCoordinate2D userLocation;
userLocation.latitude = [[userLatitudeArray objectAtIndex:i]doubleValue];
userLocation.longitude = [[userLongitudeArray objectAtIndex:i] doubleValue];
MKMapPoint * pointsArray = malloc(sizeof(CLLocationCoordinate2D)*userLongitudeArray.count);
pointsArray[i] = MKMapPointForCoordinate(userLocation);
polyline = [MKPolyline polylineWithPoints:pointsArray count:i];
free(pointsArray);
}
polyline.title = name;
[mapView addOverlay:polyline];
}
}
}
}
What I want to do is to have control on each polyline created for each user, so I can change the color of it and hide/show them on click of a button (one to show/hide my track and the other for all other users), this is why I am adding title to it.
I can see now that I am adding polyline to the same overlay, which is wrong I believe. But I don't know how many users will be there in web-service so can add multiple overlays of them.
Initially I thought I am able to remove a particular polyline with title but then I realised it is removing all as polyline.title property gets updated.
Any help would be much appreciated!
You could collect an array of those tracks that relate to other users, and keep a single track for the current user. If you clean the array at the start of the connectionDidFinishLoading function and populate it where you are currently adding the overlays to the map, then you move the addOverlay to a new function that you call at the end.
- (void) resetMap
{
if (showOtherTracks)
{
[mapView addOverlays:otherUserTracks];
} else {
[mapView removeOverlays:otherUserTracks];
}
if (showMyTrack)
{
[mapView addOverlay:myTrack];
} else {
[mapView removeOverlay:myTrack];
}
}
You can also call this when ever the button is pressed and the state changes.
My application is crashing when I scrolling map with displaying one or more annotation. Without display annotation map is working fine.
Now I can't understand what is problem here. Please Help Me.
Following is code that I used to display All annotation.
-(void)displayAllAnnotation
{
NSLog(#"%d",[reminderTitle count]);
for (int i=0 ; i<[reminderTitle count]; i++)
{
double templati=[[reminderLatitude objectAtIndex:i]doubleValue];
double templongi=[[reminderLongitude objectAtIndex:i]doubleValue];
CLLocationCoordinate2D coord;
coord.latitude=(CLLocationDegrees)templati;
coord.longitude=(CLLocationDegrees)templongi;
DDAnnotation *ann = [[[DDAnnotation alloc] initWithCoordinate:coord addressDictionary:nil] autorelease];
ann.title = [reminderTitle objectAtIndex:i];
[self.mapView addAnnotation:ann];
[ann release];
}
}
You release ann object two times so your application is crashing.
remove autorelease. just write like this:
DDAnnotation *ann = [[DDAnnotation alloc] initWithCoordinate:coord addressDictionary:nil];
Hey guys, I am having trouble getting overlays in my map view to refresh via the setNeedsDisplayInMapRect: function. Here is the relevant code:
ParkingMapViewController.m:
for (ParkingRegionOverlay *overlay in mapView.overlays) {
[overlay setNeedsDisplayInMapRect:self.mapView.visibleMapRect];
}
//...
- (MKOverlayView *)mapView:(MKMapView *)mapView
viewForOverlay:(id <MKOverlay>)overlay
{
NSLog(#"ParkingMapViewController.m mapView:viewForOverlay");
//...
}
//...
ParkingRegionOverlay.h:
#interface ParkingRegionOverlay : MKOverlayView <MKOverlay> {
MKPolygon *polygon;
MKMapRect boundingRect;
CLLocationCoordinate2D centerCoord;
//...
}
//...
And I am not getting the "ParkingMapViewController.m mapView:viewForOverlay" output to console I am expecting. I have walked through he debugger and have ensured that the for loop is being reached and executed, however mapView:viewForOverlay: isn't being called for some reason. Anyone know what I am doing wrong? Thanks in advance!
EDIT 1:
I believe I have set the delegate, coordinates, and bounding rect properly, but please take a look...
ParkingMapViewController.h
#interface ParkingMapViewController : UIViewController <MKMapViewDelegate> {
MKMapView *mapView;
//...
ParkingMapViewController.m:
//...
- (void)viewDidLoad {
[super viewDidLoad];
mapView.delegate = self;
//...
ParkingRegionOverlay.m:
//...
//initializes polygon and calculates bounding rect as well as its center coordinate
-(id)initWithPoints:(NSArray *)pointsArray andTitle:(NSString *)overlayTitle{
MKMapPoint points[[pointsArray count]];
double maxX = MIN_COORD_VAL;
double minX = MAX_COORD_VAL;
double maxY = MIN_COORD_VAL;
double minY = MAX_COORD_VAL;
double tempX = 0;
double tempY = 0;
if (self = [super init]) {
int i = 0;
//determine min/max extrema to help calculate the bounding rect
for (id coordDict in pointsArray){
tempX = [[coordDict objectForKey:#"latitude"] doubleValue];
tempY = [[coordDict objectForKey:#"longitude"] doubleValue];
maxX = fmax(tempX, maxX);
minX = fmin(tempX, minX);
maxY = fmax(tempY, maxY);
minY = fmin(tempY, minY);
CLLocationCoordinate2D coord = {tempX,tempY};
points[i] = MKMapPointForCoordinate(coord);
i++;
}//for
CLLocationCoordinate2D northWestCorner = CLLocationCoordinate2DMake(maxX, minY);
CLLocationCoordinate2D southEastCorner = CLLocationCoordinate2DMake(minX, maxY);
MKMapPoint northWestPoint = MKMapPointForCoordinate(northWestCorner);
MKMapPoint southEastPoint = MKMapPointForCoordinate(southEastCorner);
boundingRect = MKMapRectMake(northWestPoint.x, northWestPoint.y,
(southEastPoint.x-northWestPoint.x),
(southEastPoint.y-northWestPoint.y));
centerCoord = CLLocationCoordinate2DMake((maxX-minX)/2,(maxY-minY)/2);
polygon = [MKPolygon polygonWithPoints:points count:[pointsArray count]];
polygon.title = overlayTitle;
[self initAcceptedPermitsBasedOnTitle:overlayTitle];
}//if
return self;
}
//...
Thanks.
EDIT 2:
An alternate method I have tried, to no avail:
ParkingMapViewController.m
NSArray *overlayArray = [[NSArray alloc] initWithArray:[mapView overlays]];
[self.mapView removeOverlays:mapView.overlays];
[self.mapView addOverlays:overlayArray];
Removing and re-adding all overlays ain't working too well for me. It merely crashes when that third line is executed. Any ideas?
EDIT 3:
So I changed the previously posted code to the following:
NSArray *overlayArray = [mapView overlays];
[self.mapView removeOverlays:overlayArray];
[self.mapView addOverlays:overlayArray];
And am now seeing this in the console:
2011-05-05 14:24:54.145 Parking[68501:207] -[NSCFNumber boundingMapRect]: unrecognized selector sent to instance 0xa9afae0
2011-05-05 14:24:54.147 Parking[68501:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFNumber boundingMapRect]: unrecognized selector sent to instance 0xa9afae0'
Chances are you haven't set up the coordinate or boundingMapRect properties on the MKOverlay correctly. The MapView will only ask for the view if it thinks there is a possibility that it is visible, if its visible rect doesn't intersect the boundMapRect, it won't.
Also make sure your delegate for the mapView is set properly.
So I figured it out. Not necessarily the most efficient method, but it works for me. This is what I did:
[self.mapView removeOverlays:[mapView overlays]];
[self loadOverlaysAndAnnotations];
And here is loadOverlaysAndAnnotations:
- (void)loadOverlaysAndAnnotations {
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
CoreDataSingleton *coreDataSingleton = [CoreDataSingleton sharedManager];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"ParkingLot" inManagedObjectContext:[coreDataSingleton managedObjectContext]];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [[coreDataSingleton managedObjectContext] executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *overlayEntity in fetchedObjects) {
NSArray *pointsArray = [NSArray arrayWithArray:[overlayEntity valueForKey:#"overlayCoordPoints"]];
ParkingRegionOverlay *regionPolygon = [[ParkingRegionOverlay alloc] initWithPoints:pointsArray andTitle:[overlayEntity valueForKey:#"lotName"]];
[mapView addOverlay:regionPolygon];
[regionPolygon release];
NSSet *annotationsSet = [NSSet setWithSet:[overlayEntity valueForKey:#"parkingAnnotations"]];
NSArray *allAnnotations = [NSArray arrayWithArray:[annotationsSet allObjects]];
CLLocationCoordinate2D workingCoordinate;
for (ParkingAnnotations *annotation in allAnnotations) {
ParkingAnnotation *parkingAnnot = [[ParkingAnnotation alloc] init];
workingCoordinate.latitude = [[annotation latitude] doubleValue];
workingCoordinate.longitude = [[annotation longitude] doubleValue];
[parkingAnnot setCoordinate:workingCoordinate];
[parkingAnnot setTitle:[overlayEntity valueForKey:#"lotName"]];
if ([[overlayEntity valueForKey:#"lotName"] isEqualToString:#"VP 1"]) {
[parkingAnnot setLot:lot1];
}
[mapView addAnnotation:parkingAnnot];
[parkingAnnot release];
}
}
[fetchRequest release];
}//loadOverlaysAndAnnotations
In short, I didn't have to create a new function but merely call the function I used to load overlays into the map view and that works fine! Hope this helps anyone else stuck in a similar situation.
EDIT:
Important to note that I am reloading BOTH annotations and overlays, and, if done without first removing both annotations and overlays, can lead to crashing of your app if the reload function is called too many times. This is what I am currently experiencing. Just something to be aware of. To fix this I am going to have separate load functions, one for overlays, and one for annotations which will be called appropriately.
i want to show multiple pins on my MapView all with Animation of Dropping pin so how it is possible if any body have sample code then please send send link.i am new in this field.Thanks in Advance.
There are few code samples on developer.apple.com
This
is a simple map example with two pins
Just as you show single pin.. keep the code for single pin in Loop and pass different longitude latitude in loop.. You will get the pins at different location
if([points retainCount] > 0)
{
[points release];
points = nil;
}
if([annotationAry retainCount] > 0)
{
[annotationAry release];
annotationAry = nil;
}
points = [[NSMutableArray alloc]init];
annotationAry = [[NSMutableArray alloc]init];
for(int i=0;i<[longitudeary count];i++)
{
CLLocation* currentLocation1 = [[CLLocation alloc] initWithLatitude:[[latitudeary objectAtIndex:i]doubleValue] longitude:[[longitudeary objectAtIndex:i]doubleValue]];
[points addObject:currentLocation1];
}
for(int i=0;i<[points count];i++)
{
// CREATE THE ANNOTATIONS AND ADD THEM TO THE MAP
CSMapAnnotation* annotation = nil;
// create the start annotation and add it to the array
annotation = [[[CSMapAnnotation alloc] initWithCoordinate:[[points objectAtIndex:i] coordinate]
annotationType:CSMapAnnotationTypeImage
title:#"123456..."
shID:[shIDary objectAtIndex:i]
catID:[catIDary objectAtIndex:i]
ciggUse:[ciggaretteUSEary objectAtIndex:i]
wifiUse:[wifiUSEary objectAtIndex:i]
controller:self]autorelease];
[annotationAry addObject:annotation];
}
[mapViewmy addAnnotations:[NSArray arrayWithArray:annotationAry]];