I am drawing a bunch of MKPolygons in my MKMapView. Some of them lay on top of each other. How can I bring a selected polygon to the top/front?
I tried bringSubviewToFront: on a MKPolygonView I create from the polygon layer:
MKPolygonView *view = (MKPolygonView *)[self.mapView viewForOverlay:polygon];
view.strokeColor = [UIColor orangeColor];
[self.mapView bringSubviewToFront:view];
SOLUTION:
I removed
MKPolygonView *view = (MKPolygonView *)[self.mapView viewForOverlay:polygon];
view.strokeColor = [UIColor orangeColor];
[self.mapView bringSubviewToFront:view];
and replaced it with what Craig suggested:
[self.mapView insertOverlay:polygon atIndex:self.mapView.overlays.count];
which then calls the MKMapKit delegate mapView:viewForOverlay: and then I handle the color change there:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKPolygon class]] && !((MKPolygon *)overlay).isSelected) {
MKPolygonView* aView = [[MKPolygonView alloc] initWithPolygon:(MKPolygon*)overlay];
aView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
aView.strokeColor = [UIColor yellowColor];
aView.lineWidth = 3;
return aView;
}
else if ([overlay isKindOfClass:[MKPolygon class]] && ((MKPolygon *)overlay).isSelected) {
MKPolygonView* aView = [[MKPolygonView alloc] initWithPolygon:(MKPolygon*)overlay];
aView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
aView.strokeColor = [UIColor orangeColor];
aView.lineWidth = 3;
return aView;
}
}
When you add an overlay to a mapView you can choose where to place it in the list of overlays. Since an overlay can only be in the list once you can move it just by inserting again at the desired location. Since you want it at the top, this should work:
[mapView insertOverlay:overlay atIndex:[mapView.overlays count]];
You should not be calling viewForOverlay. Leave that to iOS. If you need to colour the overlay do it within viewForOverlay because iOS can and will call that when ever it wants and if you return a non-coloured overlay that's what it will draw.
Related
So I am trying to replicate the following scenario (translucent annotation views) :
And I have tried unsuccessfully the following implementations:
1- Creating a custom image with 30% opacity and adding to the map ---> Result: The image stays opaque.
Code:
-(id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
LLAnnotation *myA = (LLAnnotation*) annotation;
self.accessibilityLabel = myA.title;
self.annotation = myA;
self.enabled = YES;
self.canShowCallout = YES;
self.centerOffset = CGPointMake(5,-10);
self.backgroundColor = [UIColor yellowColor];
self.image = [UIImage imageNamed:#"circle"];
}
return self;
}`
And then adding it in - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation_
2- Adding a sublayer to the AnnotationView and clearing it ---> Result: Doesn't show any annotation.
Code:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation_
{
if (annotation_ == mapView.userLocation) return nil;
MKAnnotationView *m = [[MKAnnotationView alloc] initWithAnnotation:annotation_ reuseIdentifier:#"default"];
// m.backgroundColor = [UIColor clearColor];
CALayer *layer = [[CALayer alloc]init];
layer.frame = m.frame;
layer.backgroundColor = [UIColor lightGreenColor].CGColor;
[m.layer addSublayer:layer];
m.layer.cornerRadius = m.frame.size.width/2;
m.layer.borderWidth = 2;
m.layer.masksToBounds = YES;
return m;
}
I was thinking that adding MKOverlays on top of annotations maybe a workaround but it shouldn't be the way to go I believe.
Does anyone have other suggestions on how to implement this?
Create UIImageView object and make it looks like the image you required.
Add as subview of annotationView in viewForAnnotation delegate method will do the trick.
Also you need to set center position offset for annotation image to render annotation exactly correct position of location.
Have look on below code:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation_
{
if (annotation_ == mapView.userLocation) return nil;
MKAnnotationView *m = [[MKAnnotationView alloc] initWithAnnotation:annotation_ reuseIdentifier:#"default"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(m.center.x, m.center.y, 20, 20)];
[imageView setBackgroundColor:[UIColor colorWithRed:0.7294 green:0.7843 blue:0.1921 alpha:1.0]];
imageView.layer.cornerRadius = imageView.frame.size.width / 2;
imageView.alpha = 0.6f;
[m addSubview:imageView];
// Also set center offset for annotation
[m setCenterOffset:CGPointMake(-10, -20)];
return m;
}
What I would do is create an image in photoshop which has a transparent background, and then add your desired yellow circle on top. Then make the opacity of that circle to what ever opacity you want. Save the image as a PNG.
Once you have saved the image, add it to your Xcode project. Once you've added it, add the following line under your viewForAnnotation.
annotationView.image = [UIImage imageNamed:#"ThisIsThePNGImagesName.png"];
Hope that helps :)
I have an annotation on the map. When I select a annotation I will display callout bubble with custom view. Now when I click on the callout bubble I want to go to new view controller but callout view disappears when I tap on the view.
-(void)mapView:(MKMapView *)mapView1 didSelectAnnotationView:(MKAnnotationView *)view
{
NSLog(#"selected");
if(![view.annotation isKindOfClass:[MKUserLocation class]])
{
CustomInfoWindow *calloutView = [[[NSBundle mainBundle]
loadNibNamed:#"infoWindow"owner:self options:nil] objectAtIndex:0];
CGRect calloutViewFrame = calloutView.frame;
calloutViewFrame.origin = CGPointMake(-calloutViewFrame.size.width/2 + 15, -calloutViewFrame.size.height);
calloutView.frame = calloutViewFrame;
[calloutView.imagePlace.layer setBorderColor: [[UIColor orangeColor] CGColor]];
[calloutView.imagePlace.layer setBorderWidth: 3.0];
NSData* imageData = [[NSData alloc] initWithContentsOfURL:
[NSURL URLWithString:#"http://farm3.staticflickr.com/2926/14605349699_67a1d51b80.jpg"]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
[calloutView.imagePlace setImage:image];
[view addSubview:calloutView];
}
}
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
for (UIView *subview in view.subviews ){
[subview removeFromSuperview];
}
}
I'm no expert but you may want to add a UITapGestureRecognizer to your view.
Use the following code:
UITapGestureRecognizer *tgr = [[UITabGestureRecognizer alloc] initWithTarget:self action:#selector(showNextView)];
calloutView.addGestureRecognizer(tgr);
and
- (void)showNextView {
...
}
The bubble does not have logic, you need use callout accessory. So, use MapView delegate method:
- (void)mapView:(MKMapView *)mv annotationView:(MKAnnotationView *)pin calloutAccessoryControlTapped:(UIControl *)control;
And add callout at viewForAnnotation delegate method. For example:
UIButton *myDetailAccessoryButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
myDetailAccessoryButton.frame = CGRectMake(0, 0, 23, 23);
myDetailAccessoryButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
myDetailAccessoryButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
pinView.rightCalloutAccessoryView = myDetailAccessoryButton;
I hope my answer helps you.
I am now at a point of teaching myself the use of MapKit in Objective C. I got to the point where I can autozoom to a location that contains several annotations. I represent the annotations with the built in pins. If you click on the pin I have a an alphanumeric 2 character string to represent that spot.
I thought to myself, for better usability, why not replace the pins with the actual data. Kind of like a weathermap where they show the temperature as a pin. Is this doable?
I researched this and all I could find is this:
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
static NSString *AnnotationViewID = #"annotationViewID";
MKAnnotationView *annotationView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];
if (annotationView == nil) {
annotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID] autorelease];
}
annotationView.image = [UIImage imageNamed:#"location.png"];
annotationView.annotation = annotation;
return annotationView;
}
The problem is that I cannot and should not have to have a custom image for each combination of the two characters. Is there a way for me to draw these numbers at the location of the pin.
I found this reference:
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/LocationAwarenessPG/AnnotatingMaps/AnnotatingMaps.html
Am I on the right track. Are there some full examples I can leverage to better understand the flow.
I want to be able to select that custom pin however and segue or show more details.
Thank you for your time.
Anna, thank you for the link, I tried it and it worked. I made some changes to improve the esthetic of the label and I thought I would post my findings here for reference:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *reuseId = #"MapViewController";
MKAnnotationView *av = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (av == nil)
{
av = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 20)];
//UILabel *lbl = [[UILabel alloc] init];;
lbl.backgroundColor = [UIColor clearColor]; // This makes the background clear and just shows the text
lbl.textColor = [UIColor whiteColor]; // Use any colour you wish
lbl.alpha = 1.0; //0.5;
lbl.tag = 42;
lbl.font = [UIFont fontWithName:#"Helvetica-Bold" size:16.0]; // This is optional, I found this fond and size most readable
[av addSubview:lbl];
//Following lets the callout still work if you tap on the label...
av.canShowCallout = YES;
av.frame = lbl.frame;
} else {
av.annotation = annotation;
}
UILabel *lbl = (UILabel *)[av viewWithTag:42];
// I added this to the text to improve its visibility by essentially adding a stroke around the text. Well its a poor man's stroke by adding a shadow
lbl.layer.shadowColor = [[UIColor blackColor] CGColor];
lbl.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
lbl.layer.shadowOpacity = 1.0f;
lbl.layer.shadowRadius = 1.0f;
lbl.text = annotation.title;
return av;
}
I have a mapView with 2 circles. When I try to customize the large one the small one seems to follow the large one. For example if I customize the fill color for the large one the smaller one gets the same color. How do I make the smaller one a different color? Note: I use reusable identifiers. Thank you.. this is my working code but when i try to edit my smaller circle, the one with radius 100 it doesn't. note: this is my WORKING code as anything else i tried it failed.
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
static NSString *CircleOverlayIdentifier = #"Circle";
_mapView.delegate = self;
if ([overlay isKindOfClass:[CircleOverlay class]]) {
CircleOverlay *circleOverlay = (CircleOverlay *)overlay;
MKCircleView *annotationView =
(MKCircleView *)[mapView dequeueReusableAnnotationViewWithIdentifier:CircleOverlayIdentifier];
if (!annotationView) {
MKCircle *circle = [MKCircle
circleWithCenterCoordinate:circleOverlay.coordinate
radius:circleOverlay.radius];
annotationView = [[MKCircleView alloc] initWithCircle:circle];
//this one
}
if (overlay == self.targetOverlay) {
//adjustable
annotationView.fillColor = [UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:0.3f];
annotationView.strokeColor = [UIColor redColor];
annotationView.lineWidth = 1.0f;
} else {
//fixed
annotationView.fillColor = [UIColor colorWithWhite:0.3f alpha:0.3f];
annotationView.strokeColor = [UIColor purpleColor];
annotationView.lineWidth = 2.0f;
}
return annotationView;
}
return nil;
}
- (void)configureOverlay {
if (self.location) {
[self.mapView removeAnnotations:[self.mapView annotations]];
[self.mapView removeOverlays:[self.mapView overlays]];
CircleOverlay *overlaysmall = [[CircleOverlay alloc] initWithCoordinate:self.location.coordinate radius:100];
[self.mapView addOverlay:overlaysmall];
_targetOverlaySmall = overlaysmall;
CircleOverlay *overlay = [[CircleOverlay alloc] initWithCoordinate:self.location.coordinate radius:self.radius];
[self.mapView addOverlay:overlay];
GeoQueryAnnotation *annotation = [[GeoQueryAnnotation alloc] initWithCoordinate:self.location.coordinate radius:self.radius];
[self.mapView addAnnotation:annotation];
[self updateLocations];
}
}
I have a MKMapView with some pins on it. I connect the pins with a MKPolyline view. But the MKPolyline is only shown when I move the map (when the MapView is updated?). I want to see the MKPolyline from the beginning on.
Please inspect the following code:
-(void)plotSnapPosition {
for (id<MKAnnotation> annotation in myMapView.annotations) {
[myMapView removeAnnotation:annotation];
}
for (id<MKOverlay> overlay in myMapView.overlays) {
[myMapView removeOverlay:overlay];
}
NSArray *snaps = self.entry.snapsArray;
CLLocationCoordinate2D *locations = malloc(sizeof(CLLocationCoordinate2D) * snaps.count);
NSInteger counter = 0;
for (Snap *snap in snaps) {
locations[counter] = [snap coordinates];
CLLocationCoordinate2D c = [snap coordinates];
CAHAnnotation *annotation = [[CAHAnnotation alloc] initWithDate:snap.timeAsString coordinate:c counter:counter];
[myMapView addAnnotation:annotation];
counter++;
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:locations count:snaps.count];
MKPolylineView *routeLineView = [[MKPolylineView alloc] initWithPolyline:polyline];
routeLineView.fillColor = [UIColor redColor];
routeLineView.strokeColor = [UIColor redColor];
routeLineView.lineWidth = 5;
[myMapView setVisibleMapRect:polyline.boundingMapRect];
[self.myMapView addOverlay:polyline];
}
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKPolyline class]]) {
MKPolylineView *routeLineView = [[MKPolylineView alloc] initWithPolyline:overlay];
routeLineView.fillColor = [UIColor blueColor];
routeLineView.strokeColor = [UIColor blueColor];
routeLineView.lineWidth = 3;
return routeLineView;
}
return nil;
}
For testing issues I have set the color of the MKPolyline in the method -(void)plotSnapPosition to red. In the delegate I set it to blue. Only the blue one is shown, after moving the map around.
can someone help me out of this? I think it is only a small mistake. Thank you.
here are the screenshots:
the two pins
after moving the map:
the path after moving the map
Make sure you set the mapView's delegate before adding the overlay. So, in your case
mapView.delegate = self;
[self plotSnapPosition];
Have you tried adding a [overlayView setNeedsDisplay] call when you finish drawing?