Obj-C - All Custom MKMapView annotations not always showing on MapView? - ios

I'm trying to display multiple arrays of locations on my mapView with the below code (Restaurants, Parks, Meet Ups & Stores). When I open the app, not all addresses inside the arrays are displayed on my mapView (even though all data is returned) - e.g. sometimes only 'Stores' and 'Parks' arrays of annotations will show up on my map, but Restaurants aren't visible (even though data for all arrays is returned successfully). If I close out and reopen the app, sometimes Parks will show on the map, but nothing else. Any idea why this happens, and how I can fix it? Code updated below. Been at this forever!
Note: ParksAnnotation, RestAnnotation, meetupAnn, & StoreAnnotation are all MKPointAnnotation classes.
UPDATE (10/13/2020): I tried logging 'placemarks' under the block of code that retrieves and geocodes my 'Stores' coordinates. It appears as though self.storeData is populated, as is the created NSDictionary storeFields. That said, when I log 'placemarks', it isn't returning any coordinates, even though storeFields[#"address"] is populated. The other blocks of code seem to be doing their job just fine (ie. retrieving Meet Ups and retrieving Parks). So Meet Ups, Restaurants and Parks annotations appear fine, but all Stores annotations aren't populated. This happens to at least one type of annotation (sometimes it's Stores that's missing, other times it's Parks) at random when I launch the app. I can't for the life of me sort out why this is happening.
See below code:
MapViewController.m
#import "StoreAnnotation.h"
#import "ParksAnnotation.h"
#import "RestAnnotation.h"
#import "meetupAnn.h"
#interface MapViewController ()
#end
#implementation MapViewController
-(void)viewWillAppear:(BOOL)animated {
NSMutableDictionary *viewParamsallUsers1 = [NSMutableDictionary new];
[viewParamsallUsers1 setValue:#"hosted_meet_ups" forKey:#"view_name"];
[DIOSView viewGet:viewParamsallUsers1 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.meetUps = [responseObject mutableCopy];
int index = 0;
for (NSMutableDictionary *allMeetups in self.meetUps) {
NSString *location = allMeetups[#"where"];
NSString *userNames = allMeetups[#"node_title"];
NSString *userBio = allMeetups[#"body"];
self.alertMessage = [allMeetups[#"node_title"] mutableCopy];
CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
[geocoderFriend geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
MKCoordinateRegion region = self.mapView.region;
region.span.longitudeDelta /= 150.0;
region.span.latitudeDelta /= 150.0;
meetupAnn *meet = [[meetupAnn alloc] init];
meet.coordinate = placemark.coordinate;
meet.title = userNames;
meet.subtitle = userBio;
meet.index = index; // Store index here.
[self.mapView addAnnotation:meet];
}
}
];
index = index + 1;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
/// GRAB ALL RESTAURANT LOCATIONS ///
NSMutableDictionary *viewParams6 = [NSMutableDictionary new];
[viewParams6 setValue:#"restaurants" forKey:#"view_name"];
[DIOSView viewGet:viewParams6 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.neighbourhoodData = [responseObject mutableCopy];
int index = 0;
for (NSMutableDictionary *multiplelocationsFriend in self.neighbourhoodData) {
NSString *location = multiplelocationsFriend[#"address"];
NSString *userNames = multiplelocationsFriend[#"node_title"];
NSString *ampRemoved = [userNames stringByReplacingOccurrencesOfString:#"amp;" withString:#""];
NSString *userBio = multiplelocationsFriend[#"body"];
self.x3 = multiplelocationsFriend[#"x3"];
CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
[geocoderFriend geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
MKCoordinateRegion region = self.mapView.region;
region.span.longitudeDelta /= 150.0;
region.span.latitudeDelta /= 150.0;
RestAnnotation *point1 = [[RestAnnotation alloc] init];
point1.coordinate = placemark.coordinate;
point1.title = ampRemoved;
point1.subtitle = userBio;
point1.index = index; // Store index here.
[self.mapView addAnnotation:point1];
}
}
];
index = index + 1;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
NSString *test = self.areaName;
NSLog(#"SHOW TEST %#", test);
/// GRAB ALL STORE LOCATIONS ///
NSMutableDictionary *viewParams7 = [NSMutableDictionary new];
[viewParams7 setValue:#"stores" forKey:#"view_name"];
[DIOSView viewGet:viewParams7 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.storesData = [responseObject mutableCopy];
int index = 0;
for (NSMutableDictionary *storeFields in self.storesData) {
NSString *location = storeFields[#"address"];
NSString *userNames = storeFields[#"node_title"];
NSString *ampRemoved = [userNames stringByReplacingOccurrencesOfString:#"amp;" withString:#""];
NSString *userBio = storeFields[#"body"];
self.x3 = storeFields[#"x3"];
CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
[geocoderFriend geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
MKCoordinateRegion region = self.mapView.region;
region.span.longitudeDelta /= 150.0;
region.span.latitudeDelta /= 150.0;
StoreAnnotation *point2 = [[StoreAnnotation alloc] init];
point2.coordinate = placemark.coordinate;
point2.title = ampRemoved;
point2.subtitle = userBio;
point2.index = index; // Store index here.
[self.mapView addAnnotation:point2];
}
}
];
index = index + 1;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
/// GRAB ALL PARKS LOCATIONS ///
NSMutableDictionary *viewParams8 = [NSMutableDictionary new];
[viewParams8 setValue:#"parks" forKey:#"view_name"];
[DIOSView viewGet:viewParams8 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.parksData = [responseObject mutableCopy];
int index = 0;
for (NSMutableDictionary *parkFields in self.parksData) {
// NSLog(#"WHAT IS IN FRIENDS %#", self.friendData);
NSString *location = parkFields[#"address"];
NSString *userNames = parkFields[#"node_title"];
NSString *ampRemoved = [userNames stringByReplacingOccurrencesOfString:#"amp;" withString:#""];
NSString *userBio = parkFields[#"body"];
self.x3 = parkFields[#"x3"];
CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
[geocoderFriend geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
MKCoordinateRegion region = self.mapView.region;
region.span.longitudeDelta /= 150.0;
region.span.latitudeDelta /= 150.0;
ParksAnnotation *point3 = [[ParksAnnotation alloc] init];
point3.coordinate = placemark.coordinate;
point3.title = ampRemoved;
point3.subtitle = userBio;
point3.index = index; // Store index here.
[self.mapView addAnnotation:point3];
}
}
];
index = index + 1;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
MKCoordinateRegion mapRegion;
mapRegion.center = mapView.userLocation.coordinate;
mapRegion.span.latitudeDelta = 0.5;
mapRegion.span.longitudeDelta = 0.5;
[mapView setRegion:mapRegion animated: YES];
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 8000, 8000);
[mapView setRegion:[mapView regionThatFits:region] animated:YES];
[mapView addAnnotations:[mapView annotations]];
});
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
if([annotation isKindOfClass:[StoreAnnotation class]]) {
static NSString *identifier = #"stores";
MKAnnotationView *storesView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(storesView == nil) {
storesView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
storesView.displayPriority = MKFeatureDisplayPriorityRequired;
storesView.canShowCallout = YES;
storesView.image = [UIImage imageNamed:#"storeann4.png"];
}
else {
storesView.annotation = annotation;
}
return storesView;
}
if([annotation isKindOfClass:[meetupAnn class]]) {
static NSString *identifier = #"meetUps";
MKAnnotationView *pulsingView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(pulsingView == nil) {
pulsingView.displayPriority = MKFeatureDisplayPriorityRequired;
pulsingView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
pulsingView.image = [UIImage imageNamed:#"meetupbeacon.png"];
pulsingView.canShowCallout = YES;
}
else {
pulsingView.annotation = annotation;
}
return pulsingView;
}
if([annotation isKindOfClass:[ParksAnnotation class]]) {
static NSString *identifier = #"parks";
MKAnnotationView *parksView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(parksView == nil) {
parksView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
parksView.displayPriority = MKFeatureDisplayPriorityRequired;
parksView.image = [UIImage imageNamed:#"parksann.png"];
parksView.canShowCallout = YES;
}
else {
parksView.annotation = annotation;
}
return parksView;
}
if([annotation isKindOfClass:[RestAnnotation class]]) {
static NSString *identifier = #"rests";
MKAnnotationView *restView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(restView == nil) {
restView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
restView.displayPriority = MKFeatureDisplayPriorityRequired;
restView.image = [UIImage imageNamed:#"restann.png"];
restView.canShowCallout = YES;
}
else {
restView.annotation = annotation;
}
return restView;
}

As Ramis said, the problem is that you're setting the displayPriority before you even instantiate the pulsingView. Thus, the MKAnnotationView you subsequently instantiate never got its displayPriority set.
That having been said, I'd suggest a slightly different implementation, though. Specifically, I'd move the configuration of the annotation view into its own subclass, rather than cluttering the view controller with this sort of code:
#interface MeetUpAnnotationView: MKAnnotationView
#end
#implementation MeetUpAnnotationView
- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
self.displayPriority = MKFeatureDisplayPriorityRequired;
self.canShowCallout = YES;
self.image = [UIImage imageNamed:#"meetupbeacon.png"];
}
return self;
}
- (void)setAnnotation:(id<MKAnnotation>)annotation {
[super setAnnotation:annotation];
self.displayPriority = MKFeatureDisplayPriorityRequired;
}
#end
Then, if targeting iOS 11 or later, your view controller's viewDidLoad should register that subclass.
Now, if this is the only annotation view that you’re showing in iOS 11, you don't need a viewForAnnotation at all and can just register your default annotation view:
[self.mapView registerClass:[MeetUpAnnotationView class] forAnnotationViewWithReuseIdentifier:MKMapViewDefaultAnnotationViewReuseIdentifier];
And having registered the annotation view class, you're done. No viewForAnnotation is needed or desired.
The only time you have to implement viewForAnnotation in iOS 11 and later is if you have multiple custom annotation view types on your map. In that case, you'd register them:
[self.mapView registerClass:[MeetUpAnnotationView class] forAnnotationViewWithReuseIdentifier:meetupIdentifier];
[self.mapView registerClass:[SomeOtherAnnotationView class] forAnnotationViewWithReuseIdentifier:someOtherIdentifier];
And then use dequeueReusableAnnotationViewWithIdentifier:forAnnotation: in your viewForAnnotation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MeetupAnn class]]) {
return [mapView dequeueReusableAnnotationViewWithIdentifier:meetupIdentifier forAnnotation:annotation];
}
if ([annotation isKindOfClass:[SomeOtherAnnotation class]]) {
return [mapView dequeueReusableAnnotationViewWithIdentifier:someOtherIdentifier forAnnotation:annotation];
}
...
return nil;
}
As you can see, in iOS 11 (especially when you have only one annotation view type), the code is greatly simplified.
But if you need to support older iOS versions, you can't register the identifier and you have to use the older dequeueReusableAnnotationViewWithIdentifier:, but I'd still let the annotation view subclass take care of configuring itself. But, a more subtle problem in your code is that you are not setting the annotation in an else clause, so make sure to do that, e.g.:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MeetupAnn class]]) {
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:meetupIdentifier];
if (!annotationView) {
annotationView = [[MeetUpAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:meetupIdentifier];
} else {
annotationView.annotation = annotation;
}
return annotationView;
}
return nil;
}

You should check if some of your geocoding requests are failing inconsistently.
CLGeocoder rate limits your requests and if you do too many requests in a short time, you'll receive an error. https://developer.apple.com/documentation/corelocation/clgeocoder/1423509-geocodeaddressstring?language=objc
Check if you are getting any reason for failure inside NSError* error inside the completionHandler block.
As many times as it fails, you should see as many less number of annotations on the MapView because it is not entering the following code path.
if (placemarks && placemarks.count > 0) {
// Not entering here
}
if (nil != error) {
// Could land here
}
The only other reason would be not properly calculating map region to show all annotations in screen. Make sure you are calculating/adjusting the region correctly upon each annotation being added to the mapView.
// Define these variables globally in the view controller
CLLocationDegrees minLatitude = 90.0;
CLLocationDegrees maxLatitude = -90.0;
CLLocationDegrees minLongitude = 180.0;
CLLocationDegrees maxLongitude = -180.0;
// Call following method every time a new annotation needs to be added to the mapView
-(void)addAnnotation:(id<MKAnnotation>)annotation toMapView:(MKMapView*)mapView {
// Add the annotation to map
[mapView addAnnotation:annotation];
// Set the map region to make it visible along with all other annotations
CLLocationDegrees latitude = annotation.coordinate.latitude;
CLLocationDegrees longitude = annotation.coordinate.longitude;
minLatitude = min(minLatitude, latitude);
maxLatitude = max(maxLatitude, latitude);
minLongitude = min(minLongitude, longitude);
maxLongitude = max(maxLongitude, longitude);
CLLocationDegrees latitudeDelta = (maxLatitude - minLatitude);
CLLocationDegrees longitudeDelta = (maxLongitude - minLongitude);
CLLocationDegrees midLatitude = (maxLatitude - latitudeDelta/2);
CLLocationDegrees midLongitude = (maxLongitude - longitudeDelta/2);
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(midLatitude, midLongitude);
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
if (CLLocationCoordinate2DIsValid(center)) {
[mapView setRegion:region animated:YES];
}
}

You are assigning MKFeatureDisplayPriorityRequired to empty object. Try to assign display priority in two case:
When pulsingView is not nil.
After pulsingView allocated.
if([annotation isKindOfClass:[meetupAnn class]]) {
static NSString *identifier = #"currentLocation";
MKAnnotationView *pulsingView = (MKAnnotationView *)[self.friendsMapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(pulsingView == nil) {
pulsingView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
pulsingView.displayPriority = MKFeatureDisplayPriorityRequired;
pulsingView.canShowCallout = YES;
pulsingView.image = [UIImage imageNamed:#"meetupbeacon.png"];
NSLog(#"Location Returned");
} else {
pulsingView.displayPriority = MKFeatureDisplayPriorityRequired;
// TODO: Do map pin initial setup.
}
}

Related

Obj-C - Custom Map Annotation Callout View from XIB

I'm trying to have a custom view/XIB display when an annotation on my MapView is tapped. That said, I've found various answers to this question for swift - but none for objective C.
Currently, I'm able to display custom annotations with the following code:
ViewController.m
-(void)viewDidLoad {
NSMutableDictionary *viewParamsFriend = [NSMutableDictionary new];
[viewParamsFriend setValue:#"accepted_friends" forKey:#"view_name"];
[DIOSView viewGet:viewParamsFriend success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.friendData = [responseObject mutableCopy];
int index = 0;
for (NSMutableDictionary *multiplelocationsFriend in self.friendData) {
NSString *location = multiplelocationsFriend[#"address2"];
NSString *userNames = multiplelocationsFriend[#"node_title"];
NSString *userBio = multiplelocationsFriend[#"body"];
CLGeocoder *geocoderFriend = [[CLGeocoder alloc] init];
[geocoderFriend geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error){
if (placemarks && placemarks.count > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:topResult];
MKCoordinateRegion region = self.friendsMapView.region;
region.span.longitudeDelta /= 150.0;
region.span.latitudeDelta /= 150.0;
PointAnnotation *point = [[PointAnnotation alloc] init];
point.coordinate = placemark.coordinate;
point.title = userNames;
point.subtitle = userBio;
point.index = index; // Store index here.
[self.friendsMapView addAnnotation:point];
}
}
];
index = index + 1;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
MKAnnotationView *pinView = (MKAnnotationView*)[self.friendsMapView dequeueReusableAnnotationViewWithIdentifier:#"AnnotationIdentifier"];
if (!pinView)
{
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"AnnotationIdentifier"];
pinView.image = [UIImage imageNamed:#"mapann3.png"];
} else {
pinView.annotation = annotation;
}
pinView.canShowCallout = YES;
pinView.calloutOffset = CGPointMake(0, 0);
UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mapann3.png"]];
pinView.leftCalloutAccessoryView = iconView;
return pinView;
}
return nil;
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
UITapGestureRecognizer *tapGesture2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(calloutTappedTwo:)];
[view addGestureRecognizer:tapGesture2];
}
-(void)calloutTappedTwo:(UITapGestureRecognizer *) sender
{
MKAnnotationView *view = (MKAnnotationView*)sender.view;
id <MKAnnotation> annotation = [view annotation];
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
PointAnnotation *selectedPoint = (PointAnnotation *) view.annotation;
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
OtherUserViewController *yourViewController = (OtherUserViewController *)[storyboard instantiateViewControllerWithIdentifier:#"OtherUserViewController"];
NSMutableDictionary *dictionary = self.friendData[selectedPoint.index];
yourViewController.frienduserData = dictionary;
[self.navigationController pushViewController:yourViewController animated:YES];
}
}
That said, if I want a custom XIB I've created to appear as the callout when each annotation is tapped, where/how should I call this?

exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'

usually this code has worked perfectly for me, up until recently I went to update my project to support the latest iOS and when clicking on the map button, receive the error;
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
Prior to using Xcode 9 it would work correctly, so I am guessing the way the code works is slightly different now.
The array is NSArray *arr_OfCountryCode;
And we initialise it in the viewDidLoad
//Initialize NSArray object with Images
arr_OfCountryCode=#[#"AF",#"AL",#"DZ",#"AS",#"AD",#"AO",#"AI",#"AQ",#"AG",#"AR",#"AM",#"AW",#"AC",#"AU",#"AT",#"AZ",#"BS",#"BH",#"BD",#"BB",#"BY",#"BE",#"BZ",#"BJ",#"BM",#"BT",#"BO",#"BA",#"BW",#"BV",#"BR",#"IO",#"VG",#"BN",#"BG",#"BF",#"BI",#"KH",#"CM",#"CA",#"IC",#",CV",#"BQ",#"KY",#"CF",#"EA",#"TD",#"CL",#"CN",#"CX",#"CP",#"CC",#"CO",#"KM",#"CD",#"CG",#"CK",#"CR",#"HR",#"CU",#"CW",#"CY",#"CZ",#"CI",#"DK",#"DG",#"DJ",#"DM",#"DO",#"EC",#"EG",#"SV",#"GQ",#"ER",#"EE",#"ET",#"FK",#"FO",#"FJ",#"FI",#"FR",#"FG",#"PF",#"TF",#"GA",#"GM",#"GE",#"DE",#"GH",#"GI",#"GR",#"GL",#"GD",#"GP",#"GU",#"GT",#"GG",#"GN",#"GW",#"GY",#"HT",#"HM",#"HN",#"HK",#"HU",#"IS",#"IN",#"ID",#"IR",#"IQ",#"IE",#"IM",#"IL",#"IT",#"JM",#"JP",#"JE",#"JO",#"KZ",#"KE",#"KI",#"XK",#"KW",#"KG",#"LA",#"LV",#"LB",#"LS",#"LR",#"LY",#"LI",#"LT",#"LU",#"MO",#"MK",#"MG",#"MW",#"MY",#"MV",#"ML",#"MT",#"MH",#"MQ",#"MR",#"MU",#"YT",#"MX",#"FM",#"MD",#"MC",#"MN",#"ME",#"MS",#"MA",#"MZ",#"MM",#"NA",#"NR",#"NP",#"NL",#"NC",#"NZ",#"NI",#"NE",#"NG",#"NU",#"NF",#"KP",#"MP",#"NO",#"OM",#"PK",#"PW",#"PS",#"PA",#"PG",#"PY",#"PE",#"PH",#"PN",#"PL",#"PT",#"PR",#"QA",#"RO",#"RU",#"RW",#"RE",#"WS",#"SM",#"SA",#"SN",#"RS",#"SC",#"SL",#"SG",#"SX",#"SK",#"SI",#"SB",#"SO",#"ZA",#"GS",#"KR",#"SS",#"ES",#"LK",#"BL",#"SH",#"KN",#"LC",#"MF",#"PM",#"VC",#"SD",#"SR",#"SJ",#"SZ",#"SE",#"CH",#"SY",#"ST",#"TW",#"TJ",#"TZ",#"TH",#"TL",#"TG",#"TK",#"TO",#"TT",#"TA",#"TN",#"TR",#"TM",#"TC",#"TV",#"UM",#"VI",#"UG",#"UA",#"AE",#"GB",#"US",#"UY",#"UZ",#"VU",#"VA",#"VE",#"VN",#"WF",#"EH",#"YE",#"ZM",#"ZW",#"AX"];
When the user taps the button, a location on the map is shown at random, within the actionMethod to do that is;
str_Type = #"Randum";
self.mapView.hidden = NO;
int randomIndex = arc4random() % [arr_OfCountryCode count];
NSString *urlString = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?address=%#&sensor=false",[arr_OfCountryCode objectAtIndex:randomIndex]];
NSURL *url = [[NSURL alloc] initWithString:urlString];
I am struggling as to why the array is classed as empty when it worked for multiple iOS versions and devices prior to the latest.
It is showing as nil as soon as I check it in Xcode, other arrays initialise before it with no issue, I even tried copying an existing array, renaming it to *arr_OfCountryCode; and that still showed as nil !
In this method (entire posted below) I believe the problem is occuring.
- (IBAction)actionMethod:(UIButton *)sender {
if ([sender tag] == 0) {
self.musicView.hidden = NO;
[self.tbl_MusicTable reloadData];
} else if ([sender tag] == 1) {
str_Type = #"Randum";
self.mapView.hidden = NO;
int randomIndex = arc4random() % [arr_OfCountryCode count];
NSString *urlString = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?address=%#&sensor=false",[arr_OfCountryCode objectAtIndex:randomIndex]];
NSURL *url = [[NSURL alloc] initWithString:urlString];
[NSURLConnection sendAsynchronousRequest:[[NSURLRequest alloc] initWithURL:url] queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"%#",error.description);
} else {
NSDictionary *jsonObject=[NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableLeaves
error:nil];
NSDictionary *places = [[[[jsonObject objectForKey:#"results"] objectAtIndex:0] objectForKey:#"geometry"] objectForKey:#"location"];
NSDictionary *locations = [[[jsonObject objectForKey:#"results"] objectAtIndex:0] objectForKey:#"address_components"];
float lat = [[places objectForKey:#"lat"] floatValue];
float lon = [[places objectForKey:#"lng"] floatValue];
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta = 0.50;
span.longitudeDelta = 0.50;
location.latitude = lat;
location.longitude = lon;
region.span = span;
region.center = location;
[self.mk_MapView setRegion:region animated:YES];
NSString *str_Title = [[locations valueForKey:#"long_name"] objectAtIndex:0];
NSString *str_Short = [[locations valueForKey:#"short_name"] objectAtIndex:0];
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = location;
point.title = str_Title;
point.subtitle = str_Short;
[self.mk_MapView addAnnotation:point];
}
}];
}
}
The further code for corelocation is here
#pragma mark - CLLocationManager Delegate Method
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if ([str_Type isEqualToString:#"Custom"]) {
CLLocation *currentLocation = newLocation;
NSString *str_Longitude;
NSString *str_Latitude;
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta = 0.020;
span.longitudeDelta = 0.020;
location.latitude = currentLocation.coordinate.latitude;
location.longitude = currentLocation.coordinate.longitude;
region.span = span;
region.center = location;
[self.mk_MapView setRegion:region animated:YES];
if (currentLocation != nil) {
str_Longitude = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
str_Latitude = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
// Reverse Geocoding
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = currentLocation.coordinate;
point.title = placemark.country;
point.subtitle = placemark.locality;
[self.mk_MapView addAnnotation:point];
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
} else {
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = location;
point.title = placemark.country;
point.subtitle = placemark.locality;
[self.mk_MapView addAnnotation:point];
}
}
#pragma mark - MKMapView Delegate Methods
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *pinView = nil;
if(annotation != self.mk_MapView.userLocation)
{
static NSString *defaultPinID = #"com.invasivecode.pin";
pinView = (MKAnnotationView *)[self.mk_MapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil )
pinView = [[MKAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID];
//pinView.pinColor = MKPinAnnotationColorGreen;
pinView.canShowCallout = YES;
//pinView.animatesDrop = YES;
pinView.image = [UIImage imageNamed:#"HesOnThisMap"]; //
}
else {
//[self.mk_MapView.userLocation setTitle:#"I am here"];
}
return pinView;
}
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
MKAnnotationView *aV;
for (aV in views) {
CGRect endFrame = aV.frame;
aV.frame = CGRectMake(aV.frame.origin.x, aV.frame.origin.y - 230.0, aV.frame.size.width, aV.frame.size.height);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.45];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[aV setFrame:endFrame];
[UIView commitAnimations];
}
}
It was to do with the google maps api callbacks, it was crashing when it hit the maximum callbacks

Map Zooms Back Out after releasing on Zoom-in

I have MapView that shows two points on a map with a route. When I zoom into the map, after I release, the map zooms back out.
I have zoomEnabled and scrollEnabled all set to yes in Code and on the InterfaceBuilder
#interface MapViewController () <UIApplicationDelegate, MKMapViewDelegate,CLLocationManagerDelegate> {
CLLocationManager * locationManager;
CLPlacemark * pmDesination;
CLLocation * currentLocation;
MyAnnotation * destinationAnn;
MKPolyline *_routeOverlay;
MKRoute *_currentRoute;
}
#end
#implementation MapViewController
const static int TYPE_STATUS_PICKUP = 0;
const static int TYPE_STATUS_DROPOFF = 1;
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self stopLocationServices];
}
- (void) viewDidLoad {
[super viewDidLoad];
self.mapView.delegate = self;
self.mapView.zoomEnabled = YES;
self.mapView.scrollEnabled = YES;
[self startLocationServices];
//Show points on map
[self addressSearch:self.pickupLocation type:TYPE_STATUS_PICKUP];
[self addressSearch:self.dropoffLocation type:TYPE_STATUS_DROPOFF];
}
- (void) mapViewDidFinishLoadingMap:(MKMapView *)mapView {
[self showRoute];
NSMutableArray * pins = [[NSMutableArray alloc] init];
if (destinationAnn != nil) {
[pins addObject:destinationAnn];
}
if ([self getCurrentLocationAnnotation] != nil) {
[pins addObject:[self getCurrentLocationAnnotation]];
}
if (pins.count > 0) {
[_mapView showAnnotations:pins animated:YES];
}
}
#pragma mapping methods
- (void) addressSearch:(NSMutableDictionary *)pinLocation type:(int)type {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:pinLocation[#"address"] completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (error) {
//ERROR LOOKING UP ADDRESS
} else {
CLPlacemark * pm = [placemarks lastObject];
location.latitude = pm.location.coordinate.latitude;
location.longitude = pm.location.coordinate.longitude;
[ann setCoordinate:location];
ann.title = [pinLocation objectForKey:#"title"];
ann.subtitle = [pinLocation objectForKey:#"address"];
if (type == _toLocation) {
destinationAnn = ann;
}
[self.mapView addAnnotation:ann];
}
}];
}
#pragma mark - SHOW ROUTE
- (void) showRoute {
MKDirectionsRequest *directionsRequest = [MKDirectionsRequest new];
MKMapItem *source = [MKMapItem mapItemForCurrentLocation];
// Make the destination
CLLocationCoordinate2D destinationCoords = CLLocationCoordinate2DMake(destinationAnn.coordinate.latitude, destinationAnn.coordinate.longitude);
MKPlacemark *destinationPlacemark = [[MKPlacemark alloc] initWithCoordinate:destinationCoords addressDictionary:nil];
MKMapItem *destination = [[MKMapItem alloc] initWithPlacemark:destinationPlacemark];
// Set the source and destination on the request
[directionsRequest setSource:source];
[directionsRequest setDestination:destination];
MKDirections *directions = [[MKDirections alloc] initWithRequest:directionsRequest];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) {
NSLog(#"There was an error getting your directions: %#", error.localizedDescription);
return;
}
_currentRoute = [response.routes firstObject];
[self plotRouteOnMap:_currentRoute];
}];
}
- (void)plotRouteOnMap:(MKRoute *)route
{
if(_routeOverlay) {
[self.mapView removeOverlay:_routeOverlay];
}
// Update the ivar
_routeOverlay = route.polyline;
// Add it to the map
[self.mapView addOverlay:_routeOverlay];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
renderer.strokeColor = _toLocation ? [UIColor orangeColor] : [UIColor greenColor];
renderer.lineWidth = 4.0;
return renderer;
}
// DELGATE THAT RUNS TO SHOW CURRENT USER LOCATION
- (void) locationManager:(CLLocationManager *)manager didFailWithError:(nonnull NSError *)error {
NSLog(#"Location Services Error: %#", [error description]);
[[LoggingManager sharedReporting] addReportToLog:[NSString stringWithFormat:#"Mapping: locationManager:didFailWithError: %#",[error description] ]];
}
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations: (NSArray *)locations {
currentLocation = [locations lastObject];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
currentLocation = newLocation;
}
#pragma mark - GET TO AND CURRENT POINTS
- (NSString*) getDestination {
NSDictionary * desDict = _toLocation ? self.dropoffLocation : self.pickupLocation;
NSString * address = #"";
if ([[desDict objectForKey:#"lat"] length] > 0 && [[desDict objectForKey:#"lon"] length] > 0) {
address = [NSString stringWithFormat:#"%#,%#",[desDict objectForKey:#"lat"], [desDict objectForKey:#"lon"]];
} else if ([desDict[#"address"] length] > 0 && [desDict[#"address"] rangeOfString:#"{"].location == NSNotFound) {
address = [desDict objectForKey:#"address"];
} else {
address = #"NULL";
}
return address;
}
- (NSString*) getCurrentLocation {
return [NSString stringWithFormat:#"%f,%f", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude];
}
- (MyAnnotation*) getCurrentLocationAnnotation {
MyAnnotation * ann = [[MyAnnotation alloc] init];
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(currentLocation.coordinate.latitude, currentLocation.coordinate.longitude);
[ann setCoordinate:location];
ann.title = #"My Current Location";
ann.subtitle = #"";
return ann;
}
#end
The problem is that you are setting the map's visible region by calling showAnnotations. This conflicts with the user zooming.

iOS MapKit Annotations Not All Added

In my app, I get the latitude and longitude along with some other data from a Parse query. However, only about 100 annotations get added, when there should be 156. I verify in console that it is getting the coordinates for the missing pieces on the map, it just simply doesn't add an annotation for them. Am I missing something obvious?
-(void) viewWillAppear:(BOOL)animated {
CLLocationCoordinate2D coord = {.latitude = 15.8700320, .longitude = 100.9925410};
MKCoordinateSpan span = {.latitudeDelta = 3, .longitudeDelta = 3};
MKCoordinateRegion region = {coord, span};
[mapViewUI setRegion:region];
PFQuery *query = [PFQuery queryWithClassName:#"Share"];
[query setLimit:1000];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.mapViewData = objects;
NSLog(#"HOW MANY ARE THERE %lu", (unsigned long)[objects count]);
for (int i=0;i<[objects count];i++)
{
// Question * q = [[Question alloc]init];
PFObject * obj = [self.mapViewData objectAtIndex:i];
NSLog(#"%#", obj);
self.theObject = obj;
NSString *string = obj[#"WhereAt"];
NSArray *stringArray = [string componentsSeparatedByString: #","];
CLLocationDegrees myLatitude = [[stringArray objectAtIndex:0] doubleValue];
CLLocationDegrees myLongitude = [[stringArray objectAtIndex:1] doubleValue];
CLLocationCoordinate2D coord2 = {.latitude = myLatitude, .longitude = myLongitude};
NSLog(#"LATITUDE %#", [stringArray objectAtIndex:0]);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"MMM dd, yyyy";
MKPointAnnotation *annotation2 = [[MKPointAnnotation alloc] init];
[annotation2 setCoordinate:coord2];
[annotation2 setTitle:obj[#"FamilyName"]];
[annotation2 setSubtitle:obj[#"Result"]];
[mapViewUI addAnnotation:annotation2];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if (annotation == mapViewUI.userLocation)
{
return nil;
}
else
{
MKPinAnnotationView *annotationView = [[MKPinAnnotationView alloc] init];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"MMM dd, yyyy";
if ([annotation.subtitle isEqualToString:#"Accepted Bible"]) {
NSLog(#"Accepted");
annotationView.pinTintColor = [UIColor greenColor];
}
else if ([annotation.subtitle isEqualToString:#"Requested Different Material"]) {
NSLog(#"Different");
annotationView.pinTintColor = [UIColor blackColor];
}
else if ([annotation.subtitle isEqualToString:#"Not Home"]) {
NSLog(#"Not Home");
annotationView.pinTintColor = [UIColor yellowColor];
}
else if ([annotation.subtitle isEqualToString:#"Rejected Bible"]) {
NSLog(#"Rejected");
annotationView.pinTintColor = [UIColor redColor];
}
return annotationView;
}
return nil;
}
I don't know if this is the cause of your issue but you should be 'recycling' the annotation views like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(ClubPinAnnotation *)annotation {
MKAnnotationView *pinView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"MyPinIdentifier"];
if (!pinView) {
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"MyPinIdentifier"];
} else {
pinView.annotation = annotation;
}
// Set up the pin view properties
return pinView;
}
Also, check that your lat/longs are correct. The map view will only call for the pin views it actually needs for the current view, so if you have some that are for instance at (0,0) they won't be shown until you zoom out to include the coast of Africa.

How to add image in annotation in mkmap [duplicate]

This question already has answers here:
How to use custom icons with mapKit framework?
(2 answers)
Closed 8 years ago.
I am new to Xcode, i need to set the image in annotation point. I am trying to replace the typical iOS mapkit "pin" with a particular image. I'm new to coding so I'm not sure exactly why this isn't working, but here is what i've attempted
My code is
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *urlMapString=[NSString stringWithFormat:#"http://www.xxxx.com/xxxx_webservice/map.php?format=json&truckno=%#",nam2];
NSURL *urlMap=[NSURL URLWithString:urlMapString];
NSData *dataMap=[NSData dataWithContentsOfURL:urlMap];
NSError *errorMap;
NSDictionary *jsonMap = [NSJSONSerialization JSONObjectWithData:dataMap options:kNilOptions error:&errorMap]; NSArray *resultsMap = [jsonMap valueForKey:#"posts"];
NSArray *resMap = [resultsMap valueForKey:#"post"];
NSArray *latitudeString=[resMap valueForKey:#"latitude"];
NSString *latOrgstring = [latitudeString objectAtIndex:0];
double latitude=[latOrgstring doubleValue];
NSArray *longitudeString=[resMap valueForKey:#"longitude"];
NSString *longOrgstring = [longitudeString objectAtIndex:0];
double longitude=[longOrgstring doubleValue];
MKCoordinateRegion myRegion;
//Center
CLLocationCoordinate2D center;
center.latitude=latitude;
center.longitude=longitude;
//Span
MKCoordinateSpan span;
span.latitudeDelta=THE_SPAN;
span.longitudeDelta=THE_SPAN;
myRegion.center=center;
myRegion.span=span;
//Set our mapView
[MapViewC setRegion:myRegion animated:YES];
//Annotation
//1.create coordinate for use with the annotation
CLLocationCoordinate2D wimbLocation;
wimbLocation.latitude=latitude;
wimbLocation.longitude=longitude;
Annotation * myAnnotation= [Annotation alloc];
myAnnotation.image = [UIImage imageNamed:#"icon.gif"];
CLLocation *someLocation=[[CLLocation alloc]initWithLatitude:latitude longitude:longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:someLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSDictionary *dictionary = [[placemarks objectAtIndex:0] addressDictionary];
addressOutlet=[dictionary valueForKey:#"Street"];
City=[dictionary valueForKey:#"City"];
State=[dictionary valueForKey:#"State"];
myAnnotation.coordinate=wimbLocation;
if (addressOutlet!=NULL&&City!=NULL)
{
myAnnotation.title=addressOutlet;
myAnnotation.subtitle=[NSString stringWithFormat:#"%#,%#", City, State];
myAnnotation.image = [UIImage imageNamed:#"icon.gif"];
}
else if (addressOutlet==NULL&&City!=NULL)
{
myAnnotation.title=City;
myAnnotation.subtitle=[NSString stringWithFormat:#"%#,%#", City, State];
myAnnotation.image = [UIImage imageNamed:#"icon.gif"];
}
else if (addressOutlet!=NULL&&City==NULL)
{
myAnnotation.title=addressOutlet;
myAnnotation.subtitle=[NSString stringWithFormat:#"%#", State];
myAnnotation.image = [UIImage imageNamed:#"icon.gif"];
}
else if(addressOutlet==NULL&&City==NULL)
{
myAnnotation.title=State;
myAnnotation.subtitle=[NSString stringWithFormat:#"%#",State];
myAnnotation.image = [UIImage imageNamed:#"icon.gif"];
}
[self.MapViewC addAnnotation:myAnnotation];
}];
}
Please guide me to set the image..
Thanks in Advance
You can use mapView Delegate method.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if (annotation == mapView.userLocation)
return nil;
static NSString *s = #"identifier";
MKAnnotationView *pin = [mapView dequeueReusableAnnotationViewWithIdentifier:s];
if (!pin) {
pin = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:s];
pin.canShowCallout = YES;
pin.image = [UIImage imageNamed:#"pin.png"];
pin.calloutOffset = CGPointMake(0, 0);
}
return pin;
}

Resources