loop array of locations in separate thread - ios

I'm working on an application for the iPhone and I'm keeping track of the users current location. When the didupdateLocations delegate method actually executes i would like to test if the location in the NSArray is in a predefined array the contains other locations, this array mind you could grow over time.
I'm running a for loop within this method to test against my own array of locations but i would like to move that to a separate thread. So in case my own array with multiple locations grows to a large number the for loop does not freeze my UI.
I have tried it like this but I'm getting undesirable results. I understand that the location tracking definitely happens in a separate thread. However those didupdateLocations execute on a separate thread. The Apple doc's are not very clear on the matter. My end goal again is to compare against my array and not lock the UI.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)thisLocation {
dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// get the last object in the array of locations
CLLocation* location = [thisLocation lastObject];
dispatch_async(queue, ^{
[self checkmyArray:location];
});
}
-(void)checkmyArray:(CLLocation *)workingLocation{
NSLog(#"SoundTheAlarm");
int alarm_on_c = 0;
NSUInteger tmp_count = [theData.LocationsObjectArray count];
BOOL alarm;
NSMutableDictionary * tempObject;
CLLocationDistance distance = 0.0;
for (int i = 0; i < tmp_count; i++) {
tempObject= [theData.LocationsObjectArray objectAtIndex:i];
thisLoc = [[tempObject objectForKey:#"onoff"] isEqual:#YES];
if (thisLoc) {
//check if we are near that location
double lat = [[tempObject objectForKey:#"latitude"] doubleValue];
double lon = [[tempObject objectForKey:#"longitude"] doubleValue];
// goal = [[CLLocation alloc] initWithLatitude:40.097771 longitude:-74.941399];
goal = [[CLLocation alloc] initWithLatitude:lat longitude:lon];
// check the destination between current location and goal location - in meters
distance = [goal distanceFromLocation:workingLocation];
NSLog(#"distance %f\n", distance);
}
// if distance from goal is less than 350 meters
if (distance <= 350){
[self scheduleNotification:[tempObject objectForKey:#"name"]];
// turn off tracking for this location
[tempObject setObject:#NO forKey:#"onoff"];
[theData.LocationsObjectArray replaceObjectAtIndex:i withObject:tempObject];
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
ExtendedSavedCellTableViewCell *cell = (ExtendedSavedCellTableViewCell *)[self.tableView cellForRowAtIndexPath:path];
cell.switchView.on = NO;
// save the update to the switch to the database as well
NSString *lat = [tempObject objectForKey:#"latitude"];
/*check to determine if the uiswitch is turned off or on.*/
[self fetchedResultsController:#NO lat:lat index:path];
[self displayAlertViewForAlarm:[tempObject objectForKey:#"name"]];
}
-(void)displayAlertViewForAlarm:(NSString *)nameOfLocation{
dispatch_async(dispatch_get_main_queue(), ^(void) {
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:#"Destination reached"
message:nameOfLocation
delegate:self
cancelButtonTitle:#"Go Away"
otherButtonTitles:#"Notify", nil];
[myAlert show];
});
}

It's generally a bad idea to use threads in iOS if you can at all avoid it. In your case, I'd implement the function which does the looping to automatically pop out of the loop after too many iterations and then schedule the next chunk of iterating to happen in another pass through the event loop. In other words, something like this:
- (void) checkLocationsStartingAt:(NSNumber)start
{
NSInteger s = (start) ? [start intValue] : 0;
for (i = s; i < list.length; i++) {
if (i > TOO_MANY) {
[self performSelector:#selector(checkLocationsStartingAt:)
withObject:#(i)
afterDelay:0.001];
return;
} else {
// check element at i
}
}
}
see: NSObject Reference

You are checking one location against an array of other locations.
You display an alert for time you are closer than 350m from a location.
You could be within 350m of many items in the list.
You have no code prevent multiple alerts.
So you will sometimes have many alerts.
You have a couple options. One will probably suit your needs better than the others. Possibly one I didn't list.
You could re-use a single UIAlertView instance and check the
visible property on it. If it's already visible, do nothing.
You could break out of the loop once you get a single "hit" on being
<350m.
You could stick in a BOOL which records if you've shown the alert and
check the BOOL every time before you show the alert again.

Related

How to add new Annotation with existed annotations?

In my application, I was published recorded video from particular location. I can get the list of published location details from another service response. so Initially I got that published location details and displayed it on MapView. In MapView the pins are displayed with Cluster effect, so I used kingpin.
Here below I have loaded the Annotations on Map.
- (void)loadLocations:(NSArray *)arrayValues
{
_annotationArray = arrayValues;
[self.clusteringController setAnnotations:[self reSetannotations]];
[self.mapView setZoomEnabled:YES];
[self.mapView setCenterCoordinate:self.mapView.userLocation.coordinate];
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow];
KPGridClusteringAlgorithm *algorithm = [KPGridClusteringAlgorithm new];
algorithm.annotationSize = CGSizeMake(25, 50);
algorithm.clusteringStrategy = KPGridClusteringAlgorithmStrategyTwoPhase;
self.clusteringController = [[KPClusteringController alloc] initWithMapView:self.mapView
clusteringAlgorithm:algorithm];
self.clusteringController.delegate = self;
self.clusteringController.animationOptions = UIViewAnimationOptionCurveEaseOut;
[self.clusteringController setAnnotations:[self annotations]];
NSString * lastobjlat;
double miles;
CLLocation * location = [COMMON currentLocation];
lastobjlat = [NSString stringWithFormat:#"%f",location.coordinate.latitude];
miles = 1.;
double scalingFactor = ABS( (cos(2 * M_PI * [lastobjlat floatValue] / 360.0) ));
MKCoordinateSpan span;
span.latitudeDelta = miles/69.0;
span.longitudeDelta = miles/(scalingFactor * 69.0);
MKCoordinateRegion region;
region.span = span;
region.center = location.coordinate;
[self.mapView setRegion:region animated:YES];
self.mapView.showsUserLocation = YES;
//Call in the below selectAnnotationAction method when I came to this, after I published new one on existed or new locations
if (COMMON.isRecentPublication == YES) {
COMMON.isRecentPublication = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self selectAnnotationAction];
});
}
}
//To reset the Annotations
- (NSArray *)reSetannotations
{
NSMutableArray *annotations = [NSMutableArray array];
return annotations;
}
//Here I managed location details on my custom marker class MapAnnotation.
- (NSArray *)annotations {
CLLocationCoordinate2D locationPort;
NSMutableArray *annotations = [NSMutableArray array];
NSString *latitude, *longitude;
if ([_annotationArray count] > 0) {
for (int i = 0; i <[_annotationArray count]; i++){
latitude = [NSString stringWithFormat:#"%#",[[_annotationArray objectAtIndex:i] valueForKey:#"latitude"]];
latitude = [latitude stringByReplacingOccurrencesOfString:#"\n" withString:#""];
latitude = [latitude stringByReplacingOccurrencesOfString:#"\t" withString:#""];
longitude = [NSString stringWithFormat:#"%#",[[_annotationArray objectAtIndex:i] valueForKey:#"longitude"]];
longitude = [longitude stringByReplacingOccurrencesOfString:#"\n" withString:#""];
longitude = [longitude stringByReplacingOccurrencesOfString:#"\t" withString:#""];
latitude = [NSString replaceEmptyStringInsteadOfNull:latitude];
longitude = [NSString replaceEmptyStringInsteadOfNull:longitude];
int publicationRatio = [[[_annotationArray objectAtIndex:i] valueForKey:#"publicationRatio"] intValue];
int publicationCount = [[[_annotationArray objectAtIndex:i] valueForKey:#"publicationsCount"] intValue];
int teazLocationId = [[[_annotationArray objectAtIndex:i] valueForKey:#"objectId"] intValue];
BOOL isUpgrade = [[[_annotationArray objectAtIndex:i] valueForKey:#"isUpgraded"] boolValue];
locationPort = CLLocationCoordinate2DMake([latitude doubleValue] ,
[longitude doubleValue]);
//TODO : This is my custom annotation method
MapAnnotation *a1 = [[MapAnnotation alloc] initWithCoordinate:locationPort
tag:i
publicationRatio:publicationRatio
publicationCount:publicationCount
teazLocationId:teazLocationId isUpgraded:isUpgrade];
a1.itemindex = i + 1;
a1.publicationRatio = publicationRatio;
a1.publicationCount = publicationCount;
a1.teazLocationId = teazLocationId;
a1.isUpgraded = isUpgrade;
a1.coordinate = CLLocationCoordinate2DMake([latitude doubleValue] ,
[longitude doubleValue] );
[annotations addObject:a1];
if (COMMON.isRecentPublication == YES) {
if ([COMMON.recentPublicationLocationID isEqual: #(publishedLocationId)]) {
_recentAnnotationView = [[MKPinAnnotationView alloc] initWithAnnotation:a1 reuseIdentifier:#"cluster"];
}
}
}
}
return annotations;
}
It's not a problem to load Annotations with clustering effect on initial time. But when I published some one from same or different(new also) locations, I need to redirect Map screen(publishing section is other screen) and I need to display that publication detail with Active pin image.
Following steps I made to show the published location detail on Map
I created recentPublicationLocationID as a global variable and store the recentPublishedLocationID from response after published service.
Then this return type method - (NSArray *)annotations(after redirect to mapView I got the location details from another webservice after that it will called),
I have compared with recentPublishedId If existed or not. Then If existed, I have assigned my custom annotation (contains the recentPublished location details) to global MKPinAnnotationView instance - _recentAnnotationView
Then I directly called the didSelectPinAnnotation delegate method from this method - (void)loadLocations:(NSArray *)arrayValues like below,
//Pass recent published location details
if (COMMON.isRecentPublication == YES)
{
COMMON.isRecentPublication = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self selectAnnotationAction];
});
}
//selectAnnotationAction
- (void)selectAnnotationAction {
COMMON.isRecentPublication = NO;
COMMON.recentPublicationLocationID = nil;
[self mapView:self.mapView didSelectAnnotationView:_recentAnnotationView];
}
If I directly passed recentPublishedLocation details to didSelectAnnotationView delegate, I can only show the In Active pin instead of Active pin.
Then I debug with breakpoint why I can see the In active pin only ?
Because In this situation the didselect delegate was called and I can see the Active pin image. But it's only within sec.
Because viewForAnnotation delegate was called quickly for other pin annotations so the selected one goes to unselected state
This is the real problem. How can I overcome this work with clusters ?
Because when I displayed that published location detail correctly on map even it should be work with clustering effect. Yes I will zoom back to see the pins with cluster effect .
Finally I got the solution and achieved my requirement using ClusterKit library instead of Kingpin.
This tool really helpful me against possible to achieve append annotations and customize every thing adopt with my requirements.
So it's more helpful to me. Of course all of you.
And this tool supports Apple & Goole maps with Objective c as well as Swift.
I hope this one more helpful to other developers also.
Thanks :)

iOS Memory Issue With No Leaks

I have a problem with my app, where it consumes a lot of memory and crashes after about 40 minutes. I have run instruments to see if there were any leaks, but none appeared. Inside the app, I am using Mapbox to display maps and drawing a line annotation wherever the user has travelled. I am storing these locations in an array, removing the current annotation and presenting a new one whenever the user has moved.
In my MapViewController.m
- (void)drawCurrentPolyline
{
[self removePolylineForTrip:locationServices.trip forClass:nil];
[self addPolylineForTrip:locationServices.trip forClass:nil];
globalData.locationData = [[NSMutableArray alloc] init];
colour = [globalData convertColourFromString:[globalData.currentTrip objectForKey:#"colour"]];
[colourArray addObject:colour];
if ([currentLocations count] >= 2)
{
for (int i = [currentLocations count] - 2; i <= [currentLocations count] - 1; i++)
{
CLLocation *locationCoordinate = [[CLLocation alloc] initWithLatitude:[[currentLocations[i] objectForKey:#"latitude"] doubleValue] longitude:[[currentLocations[i] objectForKey:#"longitude"] doubleValue]];
[globalData.locationData addObject:locationCoordinate];
}
}
else
{
for (NSDictionary *location in currentLocations)
{
CLLocation *locationCoordinate = [[CLLocation alloc] initWithLatitude:[[location objectForKey:#"latitude"] doubleValue] longitude:[[location objectForKey:#"longitude"] doubleValue]];
[globalData.locationData addObject:locationCoordinate];
}
}
}
- (void)removePolylineForTrip:(int)trip forClass:(MapPreferencesTableViewController *)mapPreferencesTableViewController
{
NSMutableDictionary *dictionary = [tripAnnotions objectForKey:[NSString stringWithFormat:#"trip%d", trip]];
NSMutableArray *annotationArray = [dictionary objectForKey:#"annotions"];
for (RMAnnotation *annotation in annotationArray)
{
if ([[annotation.userInfo objectForKey:#"type"] isEqualToString:#"line"])
{
[mapView removeAnnotation:annotation];
}
}
}
- (void)addPolylineForTrip:(int)trip forClass:(MapPreferencesTableViewController *)mapPreferencesTableViewController
{
globalData.locationData = [[NSMutableArray alloc] init];
globalData.tripData = [globalData.trips objectForKey:[NSString stringWithFormat:#"trip%d", trip]];
NSMutableArray *locationArray = [globalData.tripData objectForKey:#"locationData"];
colour = [globalData convertColourFromString:[globalData.tripData objectForKey:#"colour"]];
[colourArray addObject:colour];
double lineWidth = 5.0;
NSString *type = #"line";
for (NSDictionary *location in locationArray)
{
CLLocation *locationCoordinate = [[CLLocation alloc] initWithLatitude:[[location objectForKey:#"latitude"] doubleValue] longitude:[[location objectForKey:#"longitude"] doubleValue]];
[globalData.locationData addObject:locationCoordinate];
}
if ([locationArray count] > 1)
{
RMAnnotation *annotation = [[RMAnnotation alloc] initWithMapView:mapView
coordinate:((CLLocation *)[globalData.locationData objectAtIndex:0]).coordinate
andTitle:[NSString stringWithFormat:#"Trip %d", trip]];
NSMutableDictionary *annotationData = [[NSMutableDictionary alloc] init];
[annotationData setObject:globalData.locationData forKey:#"locations"];
[annotationData setObject:colour forKey:#"colour"];
[annotationData setObject:[NSString stringWithFormat:#"%f", lineWidth] forKey:#"width"];
[annotationData setObject:type forKey:#"type"];
[annotationData setObject:[NSString stringWithFormat:#"%d", trip] forKey:#"trip"];
annotation.userInfo = annotationData;
// NSLog(#"User Info For Annotation:\n%#", annotation.userInfo);
//annotation.layer = [NSString stringWithFormat:#"%d", trip];
[annotation setBoundingBoxFromLocations:globalData.locationData];
// Add annotation for tracking
[self addAnnotationToTripArray:annotation forTrip:trip];
[mapView addAnnotation:annotation];
}
}
I think the problem might be the fact that I am storing each of the user locations in a mutable array. Hence, I told the app to remove everything in the array once a memory warning has appeared. However, the app still crashes. I cannot seem to find the source of the memory consumption. There are no leaks (except for some small core graphics ones).
Could someone please guide me from where to go from here, or help me find the source of this issue? The simulator used over 1GB of data, and the app became very slow.
Remember, it's not a leak if it's not memory that's been orphaned somehow. Pay attention to your memory usage over time. Instruments will tell you the number of objects living and what kind they are. Things to look out for are things like large arrays of things growing over time and UIView's not getting removed from the superViews.
I had a similar issue and it was because I had MBProgressHUD instances that were hidden, but never removed from their superView.
As #InkGolem states, increasing memory is not necessarily a leak.
Use instruments to check for leaks and memory loss due to retained but not leaked memory. The latter is unused memory that is still pointed to. Use Mark Generation (Heapshot) in the Allocations instrument on Instruments.
For HowTo use Heapshot to find memory creap, see: bbum blog
Basically there method is to run Instruments allocate tool, take a heapshot, run an intuition of your code and another heapshot repeating 3 or 4 times. This will indicate memory that is allocated and not released during the iterations.
To figure out the results disclose to see the individual allocations.
If you need to see where retains, releases and autoreleases occur for an object use instruments:
Run in instruments, in Allocations set "Record reference counts" on on (you have to stop recording to set the option). Cause the picker to run, stop recording, search for there ivar (datePickerView), drill down and you will be able to see where all retains, releases and autoreleases occurred.

How can I improve this performance?

I have a UITabBarController based app that:
1) Fetches data from web and parses it into CD (this works fine). [In Tab 1]
2) Then when second tab [In Tab 2] is selected, it runs this method:
{ viewDidLoad > loadRecordsFromCD (performBlockAndWait) > populateLocationsToSort }
- (void)populateLocationsToSort {
//1. Get UserLocation based on mapview
self.userLocation = [[CLLocation alloc] initWithLatitude:self.userLocation.coordinate.latitude longitude:self.userLocation.coordinate.longitude];
// Loop thru dictionary-->Create locations
// 2. Loop thru dictionary to get Custom Objects
for (Location * locationObject in self.farSiman) {
// 3. Unload objects values into locals
//PARSE ALL DATA
NSString *coordenadas = locationObject.coordenadas;
NSArray *coordinatesArray = [coordenadas componentsSeparatedByString:#","];
NSString * latitude = [coordinatesArray objectAtIndex:0];
NSString * longitude = [coordinatesArray objectAtIndex:1];
NSString * storeDescription = locationObject.nombrePublico;
NSString * address = locationObject.direccion;
NSString * ciudad = locationObject.ciudad;
NSString * horario = locationObject.horario;
NSString * hor_LV = locationObject.hor_LV;
NSString * hor_S = locationObject.hor_S;
NSString * hor_D = locationObject.hor_D;
NSString * telefono = locationObject.telefono;
NSString * celular_TA = locationObject.celular_TA;
NSString * celular_TB = locationObject.celular_TB;
NSString * hrs24 = locationObject.hrs24;
NSString * driveThru = locationObject.driveThru;
//NSString * estado = locationObject.estado;
NSString * estado;
// IF self.open24hrs SELECTED
if (self.open24hrs) {
// Set it based on TimeComparator
if ([TimeComparator dealWithTimeStrings2:locationObject.hor_LV]) {
estado = #"Abierta";
} else {
estado = #"Cerrada";
}
} else {
estado = locationObject.estado;
}
// 4. Create MyLocation object based on locals gotten from Custom Object
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
MyLocation *annotation = [[MyLocation alloc] initWithName:storeDescription address:address coordinate:coordinate distance:0 ciudad:ciudad horario:horario telefono:telefono hrs24:hrs24 driveThru:driveThru hor_LV:hor_LV hor_D:hor_D hor_S:hor_S celular_TA:celular_TA celular_TB:celular_TB estado:estado];
// 5. Calculate distance between locations & uL
CLLocation *pinLocation = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
CLLocationDistance calculatedDistance = [pinLocation distanceFromLocation:self.userLocation];
annotation.distance = calculatedDistance/1000;
//Add annotation to local NSMArray
[self.annotationsToSort addObject:annotation];
} //ENDS FOR LOOP
//SORT the created annotationsToSort
[self sort];
}
It takes the array populated from the CD fetch and creates objects out of them. It creates new objects because it must take the hor_LV field, parse it into dates and compare them to now in order to determine the location.estado.
Currently there are 84 records being fetched and I can already notice a lag from the time i tap on the second Tab, (this tableviewcontroller), and the time it actually displays onscreen.
I can't pre-parse this array because the user sets some filters on Tab 1 which are passed in to Tab 2 before the data is fetched from the database. So I know that the fetch must occur as Tab 2 loads. My question is, what could I do to speed this up or not let the lag be so obvious?
The best would be to use threads to deal with the data faster. Also, it would give you the opportunity to give some feedback to the user making the lag almost unnoticeable or at least less annoying - especially if you consider sending data even if all is not processed yet.
If you're looking for code optimizations, I would suggest that you use a profiler and modify the code accordingly.
Look at using a batched fetch request. You can't display all of the 84+ items to the user at the same time so you don't need to pull them all out of the data store at the same time. The fetch request can be configured to sort the items and to return pages suitable for the number of items that can be seen at any one time. Then each time a page is loaded there will be a very small amount of processing / conversion but the overall cost will be distributed. Also, if the user never 'scrolls' to see data then it won't be loaded.

how to alter NSMutableArray in an specific array

I have a NSMutableArray wich holds a Custom class of MKMapkit.
MapPoint *placeObject = [[MapPoint alloc] initWithTitle:title subtitle:subtitle coordinate:loc description:description storeImage:storeImage];
[annotationArray addObject:placeObject];
My routine fills the placeObject without the image because they will load asynchronously and finished after a couple of seconds. Now my questions is: Is there a way to alter the placeObject which is within the annotationArray?
EDIT
To display my issue in a better way here some code parts:
if (annotationArray == nil)
{
[self setAnnotationArray:[[NSMutableArray alloc] init]];
}
for (NSDictionary *locationDetails in parser.items)
{
__block NSData *storeImageData;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
storeImageData = [NSData dataWithContentsOfURL:storeImageURL];
dispatch_sync(dispatch_get_main_queue(), ^{
UIImage *storeImageTMP = [UIImage imageWithData:storeImageData];
counterBlock ++;
NSLog(#"async: %d", counterBlock);
[imageArray addObject:storeImageTMP];
});
});
MapPoint *placeObject = [[MapPoint alloc] initWithTitle:title subtitle:subtitle coordinate:loc description:description storeImage:storeImage];
[annotationArray addObject:placeObject];
}
[mapView addAnnotations:annotationArray];
after this is done: dispatch_queue_t will start working. So I check every 2 seconds if its fully loaded. Then i start to alter annotationArray and refresh my annotations (remove all -> add to map witch additional image)
- (void) checkMap
{
if (counterBlock == 547)
{
for (int i=0; i<=counterBlock; i++)
{
MapPoint *point = annotationArray[i];
point.storeImage = imageArray[i];
}
if(timer)
{
[timer invalidate];
timer = nil;
}
[mapView removeAnnotations:mapView.annotations];
[mapView addAnnotations:annotationArray];
}
}
All you need to do is get a reference to one of the MapPoint objects in the array. Then you can set properties and/or call methods on the object as needed.
MapPoint *point = annotationArray[x]; // x is whatever index you need
point.image = ... // some image
You can retrieve from your mutable array, the object you want to alter with the objectAtIndex method
NSInteger indexofMyObject = 0;
MapPoint *point = [myArray objectAtIndex:indexofMyObject];
Your MapPoint instance placeObject is a pointer. So adding it to the array does not copy placeObject by value, but actually holds its address. Getting the address of the object from the array will allow you to manipulate it.
annotationArray[objectIndex].anything = anything you want

multithreading in iOS, MapKit displaying data

My app is MapKit based, where multiple users can be tracked. Now using our web services, I am displaying my location on the map plus other users' last, let's say 10 locations. If a user updates their location, it is sent through the web service and displayed on the maps via call back. I am able to track other users in real time but don't know how to use Threading here. My UI is blocking at times and also crashing sometimes due to memory issue.
In my connectionDidFinishLoading method, I am parsing JSON data and then creating annotations and overlay:
-(void) connectionDidFinishLoading: (NSURLConnection *) connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSArray *trackingDict = [NSJSONSerialization JSONObjectWithData:empJsonData options:kNilOptions error:nil];
NSLog(#"Json Dictionary = %#", trackingDict);
NSLog(#"COUNT = %i",trackingDict.count);
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++) {
NSLog(#"COunt - > %#", [[latlongArray objectAtIndex:i]objectForKey:#"lat"]);
NSLog(#"COunt - > %#", [[latlongArray objectAtIndex:i]objectForKey:#"long"]);
[userLatitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"lat"]];
[userLongitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"long"]];
}
// ProfilePIC URL
profilePicURLString = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_profilePicture"];
NSString *name = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_firstName"];
[userNameArray addObject:name];
[profilePicURLStringArray addObject:profilePicURLString];
for (int i = 0; i<userLatitudeArray.count; i++) {
CLLocationCoordinate2D userLocation;
userLocation.latitude = [[userLatitudeArray objectAtIndex:i]doubleValue];
userLocation.longitude = [[userLongitudeArray objectAtIndex:i] doubleValue];
Annotation *Anno = [[Annotation alloc]init];
Anno.coordinate = userLocation;
Anno.title = name;
Anno.userProfileImageString = profilePicURLString;
[mapView addAnnotation:Anno];
}
NSLog(#"ARRAY for longitude %#", userLongitudeArray);
NSLog(#"ARRAY for latitude %#", userLatitudeArray);
int i;
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);
}
[mapView addOverlay:polyline];
}
}
[mapView reloadInputViews];
}
}
The web service is called after every 20 seconds, I know I can user GCD here or other threading approach but at the time-interval when web service is called via background thread, the annotations and overlays are not displayed not the map.
Any help is much appreciated!
I don't think there's an quick & easy fix for this. One approach would be to separate the code that collects (modifies) the data from the code that updates the view.
Set the code that is dealing with UI into a separate method, updateUI for example.
From here on you have couple of choices. You could try this for example:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//perform the data collection, calculations...
//here is where the model in MVC gets modified
//
[self performSelectorOnMainThread: #selector(updateUI) withObject:nil waitUntilDone: NO];
}
-(void)updateUI
{
//do the UI updates (like adding overlays etc...) here
}
You could also store all the data needed to update the UI to a kind of object and pass it as withObject: parameter.

Resources