MKPolyline / MKPolylineRenderer changing color without remove it - ios

I working around with map app, I want to ask how to change the polyline color without remove and add it again, I found this topic https://stackoverflow.com/questions/24226290/mkpolylinerenderer-change-color-without-removing-overlay in stackoverflow but this is not involve with my question, I did not touch the line, so no need to do with -[MKMapViewDelegate mapView:didSelectAnnotationView:]
So is it possible to do that?
EDIT: What I want to do is change the polyline color smoothly (by shading a color - sound like an animation) If you have any idea on how to animate this polyline please also tell me too. Thanks

Complex animations or shading/gradients will probably require creating a custom overlay renderer class.
These other answers give ideas about how to draw gradient polylines and animations will most like require a custom overlay renderer as well:
how to customize MKPolyLineView to draw different style lines
Gradient Polyline with MapKit ios
Draw CAGradient within MKPolyLineView
Apple's Breadcrumb sample app also has an example of a custom renderer which you may find useful.
However, if you just want to update the line's color (say from blue to red), then you may be able to do that as follows:
Get a reference to the MKPolyline you want to change.
Get a reference to the MKPolylineRenderer for the polyline obtained in step 1. This can be done by calling the map view's rendererForOverlay: instance method (not the same as the mapView:rendererForOverlay: delegate method.
Update the renderer's strokeColor.
Call invalidatePath on the renderer.
Not sure what you want but you may be able to "animate" the color going from blue to red by changing the color and calling invalidatePath gradually in timed steps.
Another important thing is to make sure the rendererForOverlay delegate method also uses the line's "current" color in case the map view calls the delegate method after you've changed the renderer's strokeColor directly.
Otherwise, after panning or zooming the map, the polyline's color will change back to whatever's set in the delegate method.
You could keep the line's current color in a class-level variable and use that in both the delegate method and the place where you want to change the line's color.
An alternative to a class-level variable (and probably better) is to either use the MKPolyline's title property to hold its color or a custom polyline overlay class (not renderer) with a color property.
Example:
#property (nonatomic, strong) UIColor *lineColor;
//If you need to keep track of multiple overlays,
//try using a NSMutableDictionary where the keys are the
//overlay titles and the value is the UIColor.
-(void)methodWhereYouOriginallyCreateAndAddTheOverlay
{
self.lineColor = [UIColor blueColor]; //line starts as blue
MKPolyline *pl = [MKPolyline polylineWithCoordinates:coordinates count:count];
pl.title = #"test";
[mapView addOverlay:pl];
}
-(void)methodWhereYouWantToChangeLineColor
{
self.lineColor = theNewColor;
//Get reference to MKPolyline (example assumes you have ONE overlay)...
MKPolyline *pl = [mapView.overlays objectAtIndex:0];
//Get reference to polyline's renderer...
MKPolylineRenderer *pr = (MKPolylineRenderer *)[mapView rendererForOverlay:pl];
pr.strokeColor = self.lineColor;
[pr invalidatePath];
}
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolyline class]]) {
MKPolylineRenderer *pr = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
pr.strokeColor = self.lineColor;
pr.lineWidth = 5;
return pr;
}
return nil;
}

Yous hould look at MKOverlayPathRenderer method
- invalidatePath.
From the doc, it says:
Call this method when a change in the path information would require
you to recreate the overlay’s path. This method sets the path property
to nil and tells the overlay renderer to redisplay its contents.
So, at this moment, you should be able to change your drawing color.

Related

Two coloured/custom line on MKPolylineRenderer

I am drawing x amount of lines on an MKMapView. The data is being downloaded from a webservice and for each line that needs drawing I am creating a MKPolyline, adding it to an overlayArray (some overlays have more than one polyline) and finally adding that overlay to the map via:
[self.mapView addOverlays:overlayArray];`
The next function is therefore called which works wonderfully:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if (![overlay isKindOfClass:[MKPolygon class]]) {
MKPolyline *route = overlay;
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:route];
renderer.strokeColor = [lineColours objectForKey:route.title];
renderer.lineWidth = 3.0;
return renderer;
} else {
return nil;
}
}
As you can see, the strokeColor gets the correct UIColor from a pre-defined NSMutableDictionary. This all works as expected.
However, some of the lines overlap and using a single colour is not always desired. I was wondering if there was a way of creating a line made up of two or even 3 colours, but not a gradient along the line as seen in many fitness apps, but colours across the line. For example, another use would be to draw a motorway route made up of two white lines with a transparent strip in the middle.
This is quite important for an app I am developing so I will post any findings I find here, and I hope that other people can share their thoughts and knowledge too.

Make GMSCircle respond to tap?

I am using the Google Maps API for iOS and I want to make it so when you tap on a GMSCircle it pops up a little thing I coded elsewhere. I have set the circle to "tappable" but I cannot find what I need to set or make to listen for the tap. What do I use?
CLLocationCoordinate2D circleCenter = CLLocationCoordinate2DMake(10,10);
GMSCircle *circ = [GMSCircle circleWithPosition:circleCenter
radius:10];
circ.tappable = true;
[circ setFillColor:[UIColor colorWithRed:1 green:0 blue:0 alpha:.5]];
circ.map = mapView_;
You need to use the delegate method didTapOverlay :
- (void) mapView: (GMSMapView *) mapView didTapOverlay: (GMSOverlay *) overlay
Here the parameter overlay indicates the overlay that was tapped. So you need to check if it equals circ.
EDIT : Adding details on how to check for circle within didTapOverlay
When GMSCircle is added to the map, a corresponding GMSPolygon is also created. If the circle is set as tappable, then on tapping it, the overlay passed to the didTapOverlay method is this related polygon and not the circle . So a direct comparison between the overlay and the circle is not possible. Hence as Raspu as pointed out, you can set a value in title using circ.title = and then inside didTapOverlay, you can check if overlay.title is same as circ.title. This works because the title property of the circle is within the corresponding polygon and hence will be present in the overlay parameter.

MKPolyLine, detect when lines overlap and change color accordingly

I am designing a transit app that overlays several routes in the form of MKPolyLines on a map. Currently the colors of the various routes are set as the title property of MKPolyLine. I'm wondering if there is a way to detect when lines of different colors overlap, and then change the color. Currently when two routes are added on top of each other, the color is simply the last one added.
My attempt at the pseudocode
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
MKPolyline *polyline = (MKPolyline *)overlay;
UIColor *color = [self colorWithHexString:polyline.title];
MKPolylineView *polylineView = [[MKPolylineView alloc] initWithPolyline:overlay];
if the polyline matches an already existing polyline{
color = new color
}
polylineView.strokeColor = color;
polylineView.lineWidth = 5.0;
}
Seems simple enough? Not sure if it is possible to compare polylines and see if one is already on the map, might not be an exact enough identifier. Thanks for your help.
I ended up using the lineDashPattern property of MKPolylineView. Applying this to one of the overlapping lines achieves the desired effect.

iOS: How to catch a tap event on a marker on a map that has a CALayer on top of it?

I have a mapView (RouteMe mapView) on which there are annotations (markers).
On the mapView there is a touchesEnded function on which I usually catch all events.
Some markers have an added layer on top of them.
That layer has some animation images and as much as I know this is the only way I can show these animation images on top of the markers.
The problem is that I don't know how I can intercept a touch on a marker that has that layer on top of it. When I test the hit on the touchesEnded I recognize a CALayer class rather than a RMMArker class (obv0iously, because the layer is on top of the marker, therefore is first to intercept the event).
How can I reach the Marker once the top CALayer is tapped?
Thanks
Hackish workaround: Create an RMMapLayer instead of a CALayer. Remember to set the annotation on the sublayer to get things like callouts to work, e.g. in your RMMapLayer subclass:
RMMapLayer *sublayer = [[RMMapLayer alloc] init];
sublayer.annotation = ann;
sublayer.contents = (id)img2.CGImage;
sublayer.contentsScale = img2.scale;
sublayer.position = CGPointMake(CGRectGetWidth(self.bounds)/2, CGRectGetHeight(self.bounds)/2);
sublayer.bounds = CGRectMake(0, 0, img2.size.width, img2.size.height);
[self addSublayer:sublayer];
I don't know how many other things this has potential for breaking though, so you can follow this issue for any updates:
https://github.com/mapbox/mapbox-ios-sdk/issues/190
You can instruct the touches to passthrough. Take a look at this question: How can I click a button behind a transparent UIView?
Since the CALayer was on top of a CALAyer, all it took was to access it ancestor blockingLayer.superlayer.
By checking on the ancestor, I could have all that I needed.
This solved the problem.

Change coordinate of MKOverlay for an MKOverlayView

I have an overlay on the map, and I would like to change its coordinates. To do this seamlessly I'm going to call the setNeedsDisplayInMapRect: method after the change has been made to the view.
I've tested this out by just changing the fillColor and it works fine:
overlayView.fillColor = [[UIColor greenColor] colorWithAlphaComponent:0.3];
[overlayView setNeedsDisplayInMapRect:mapView.visibleMapRect];
However I've seemingly hit a brick wall trying to also change the center coordinates of my overlay view (which is an MKCircleView with an MKCircle). There is a method in
MKAnnotation, which MKCircle conforms to, called setCoordinate: - which seems like what I need. Unfortunately though, the circle property in MKCircleView is readonly. Moreover the overlay property in MKOverlayView is also readonly.
Is there actually a way of changing the coordinates for an overlay, without resort to remove the overlay view and adding a new one (which would cause very noticeable flicker on the screen.) ?
same problem is occurred here , so i'm creating set of method and calling it according to requirement.
-(void)removeAllAnnotationFromMapView{
if ([[self.tmpMapView annotations] count]) {
[self.tmpMapView removeAnnotations:[self.tmpMapView annotations]];
}
}
-(void)removeAllOverlays{
if ([[self.tmpMapView overlays] count]) {
[self.tmpMapView removeOverlays:[self.tmpMapView overlays]];
}
}
-(void)removeOverlayWithTag:(int)tagValue{
for (MKOverlayView *oView in [self.tmpMapView overlays]) {
if (oView.tag == tagValue) {
[self.tmpMapView removeOverlay:oView];
}
}
}

Resources