Question

I'm using MKMapView and MKPolyline to display a route shape. When an annotation is placed on my MKMapView and there's another annotation already there, it will asynchronously dispatch a routing call to my directions provider and add the shaped overlay.

Here's the problem. If I use the main thread for the request, it will freeze the MKAnnotation drop while the request is underway, but when it returns, it display the overlay nicely. If instead I dispatch the request to an async queue, there's no freeze in the annotation drop, and when the async calls finalises, it adds the overlay, but it takes a while for the MapView to realise that and actually draw the overlay. Sometimes it takes like 20secs. Here's the code.

//Action handler selector to drop the pin
-(void)dropWayPoint:(WayPoint*)wp prevWayPoint:(WayPoint*)prevWp {
    [self.mapView addWayPoint: wp];
    [self.routeService routeFrom:prevWp to:wp];
}

And the actually route calculation is here:

@implementation RouteService

static dispatch_queue_t queue;

+(RouteService) initRouteService:(id)delegate {
    RouteService *rs = [[RouteService alloc] init];
    //Lots of things happen here, including the queue creation
    ...
    if (!queue) {
        queue = dispatch_queue_create("RouteDispatch.queue", NULL);
    }
    return rs;
}
//Lots of methods...
//then
-(void)routeFrom:(WayPoint*)wp to:(WayPoint*)wpTp {
    dispatch_async(queue, ^{
        //Code here
        DirectionsRouteRequest *rt = [DirectionsRouteRequest buildWithRouteType:@"fastest"];
        [rt addObjectToLocation:[wp coord]];
        [rt addObjectToLocation:[wpTp coord]];
        rt.options.routeType = @"fastest";

        NSLog(@"Dispatching...");

        //Now send the request
        DirectionsResponseType *response = [MapUtil computeDirections:rt];

        Leg *leg = [Leg buildFromResponse:response route: self.route startWayPoint:wp endWayPoint:wpTp];
        MKPolyline *pl = [MapUtil makePolylineWithLocations:[leg routeShape]];
        [self.route.legsHash setObject:pl forKey:leg.legIdStr];

        //Add Overlay here!!!
        [self.mapDeskViewController.mapView addOverlay: pl];
        //Desperately advising map view to redraw itself and show the overlay
        [self.mapDeskViewController.mapView setNeedsDisplay];

        //I can see this being displayed very quickly, meaning 
        NSLog(@"Response, pl_count:%d",pl.pointCount);

        //However it takes a long time after this returns to actually display the overlay.
    });
}

If I take the above code and comment out the async directives, it takes the same time to process it out, provokes an annoying freezing, but the overlay is drawn straight away:

//Lots of methods...
//then
-(void)routeFrom:(WayPoint*)wp to:(WayPoint*)wpTp {
    /* COMMENTED OUT ASYNC
    dispatch_async(queue, ^{
    */
        //Code here
        DirectionsRouteRequest *rt = [DirectionsRouteRequest buildWithRouteType:@"fastest"];
        [rt addObjectToLocation:[wp coord]];
        [rt addObjectToLocation:[wpTp coord]];
        rt.options.routeType = @"fastest";

        NSLog(@"Dispatching...");

        //Now send the request
        DirectionsResponseType *response = [MapUtil computeDirections:rt];

        Leg *leg = [Leg buildFromResponse:response route: self.route startWayPoint:wp endWayPoint:wpTp];
        MKPolyline *pl = [MapUtil makePolylineWithLocations:[leg routeShape]];
        [self.route.legsHash setObject:pl forKey:leg.legIdStr];

        //Add Overlay here!!!
        [self.mapDeskViewController.mapView addOverlay: pl];
        //Desperately advising map view to redraw itself and show the overlay
        [self.mapDeskViewController.mapView setNeedsDisplay];

        //I can see this being displayed very quickly, meaning 
        NSLog(@"Response, pl_count:%d",pl.pointCount);
    /*
    COMMENTED OUT ASYNC
    });
    */
}

Any idea why it takes a while for MKMapView to realise it needs to draw the overlay when it's done asynchronously?

Thanks Aurelio

Was it helpful?

Solution

You need to update the map view (and all UI) on the main thread. So, within your dispatch_async block and after receiving your response:

   // Create another block that gets queued up in the main_queue, a default serial queue
   dispatch_async(dispatch_get_main_queue(), ^{

       Leg *leg = [Leg buildFromResponse:response route: self.route startWayPoint:wp endWayPoint:wpTp];
       MKPolyline *pl = [MapUtil makePolylineWithLocations:[leg routeShape]];
       [self.route.legsHash setObject:pl forKey:leg.legIdStr];

       //Add Overlay here!!!
       [self.mapDeskViewController.mapView addOverlay: pl];
       //Desperately advising map view to redraw itself and show the overlay
       [self.mapDeskViewController.mapView setNeedsDisplay];

       //I can see this being displayed very quickly, meaning 
       NSLog(@"Response, pl_count:%d",pl.pointCount);
    });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top