MKPolylineView not rendered on MKMapView till MKMapView is clicked - ios

I have a view controller, where I initialize a mkmapview programatically, and set the view controller to be the delegate of mkmapview. Also, this mapview is added as a subview to scrollview in the view controller class.
I add a mkpolyline to mkmapview, and I have
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
However, viewforoverlay is not called when view controller is loaded, but it is called when I click/touch mapview and drag/move it around

This problem is typically seen if you add the MKOverlay to the MKMapView prior to assigning the delegate. When you add the MKOverlay to the MKMapView, the delegate method mapView:viewForOverlay: is called. If you have not assigned the delegate by that time, you will not see the overlay renderer in the initial render.

Try adding this line after you add your MKPolylineView overlay:
[myMapView setNeedsRedisplay];
This is how I add a route to my map:
-(void)drawRoute
{
...
routeLine = [MKPolyline polylineWithPoints:pointArr count:totalPoints];
[geoMap addOverlay:routeLine];
....
}
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKPolylineView *lineView = [[[MKPolylineView alloc] initWithPolyline:overlay] autorelease];
UIColor *lineColor = [[UIColor alloc] initWithRed:0 green:0 blue:0 alpha:1.0];
lineView.fillColor = lineColor;
lineView.strokeColor = lineColor;
[lineColor release];
lineView.lineWidth = 12;
lineView.alpha = 1;
return lineView;
}

Related

MKAnnotationView Subviews

Currently, I am having an issue with my project in implementing a custom MKAnnotationView that has multiple custom UIImageViews. So these custom UIImageViews have a clear button on top of them to not have to add gesture recognizers.
As you can see, it would be beneficial to actually tap the MKAnnotationView subviews and have some action happen.
I implemented a protocol for the MKAnnotationView where each image subview within the MKAnnotationView makes a callback to the controller that is the owner of the MKMapView... Heres the code...
PHProfileImageView *image = [[PHProfileImageView alloc] initWithFrame:CGRectMake(newX - radius / 5.0f, newY - radius / 5.0f, width, height)];
[image setFile:[object objectForKey:kPHEventPictureKey]];
[image.layer setCornerRadius:image.frame.size.height/2];
[image.layer setBorderColor:[[UIColor whiteColor] CGColor]];
[image.layer setBorderWidth:2.0f];
[image.layer setMasksToBounds:YES];
[image.profileButton setTag:i];
[image.profileButton addTarget:self action:#selector(didTapEvent:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:image];
- (void)didTapEvent:(UIButton *)button
{
NSLog(#"%#", [self.pins objectAtIndex:button.tag]);
if (self.delegate && [self.delegate respondsToSelector:#selector(didTapEvent:)]) {
[self.delegate JSClusterAnnotationView:self didTapEvent:[self.pins objectAtIndex:button.tag]];
}
}
So as you can see, I already attempt to log the result of the tapped image but nothing :(. Is the way I'm implementing this not the way to go? Am I supposed to have CAShapeLayers or something? Not really sure at this point. Anyone got any ideas?
Edit
Im thinking that I might have to implement a custom callout view. Since a callout view actually adds buttons to its view and can respond to touch events... Not totally sure though because callouts are only shown once the annotation view is tapped. And in this case, the ACTUAL annotation view is the middle label
So I resized the mkannotationview's frame to a much larger frame and apparently all the subviews are actually not within the MKAnnotationView's bounds, so the subviews aren't actually being tapped. Now that Im thinking about this solution, it probably wasn't the best solution.
If anyone has any suggestions rather than adding subviews to a MKAnnotationView to create the view I currently have, that would be great!
For the Custom AnnotationView with Clickable Buttons, you have to create custom AnnotationView SubClass in the Project. For that create a new file.
And add these two methods to the implementation file.
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hitView = [super hitTest:point withEvent:event];
if (hitView != nil)
{
[self.superview bringSubviewToFront:self];
}
return hitView;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
CGRect rect = self.bounds;
BOOL isInside = CGRectContainsPoint(rect, point);
if(!isInside)
{
for (UIView *view in self.subviews)
{
isInside = CGRectContainsPoint(view.frame, point);
if(isInside)
break;
}
}
return isInside;
}
Then go to the ViewController.m file again and modify the viewDidLoad method as this.
- (void)viewDidLoad {
[super viewDidLoad];
self.mapKit.delegate = self;
//Set Default location to zoom
CLLocationCoordinate2D noLocation = CLLocationCoordinate2DMake(51.900708, -2.083160); //Create the CLLocation from user cordinates
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(noLocation, 50000, 50000); //Set zooming level
MKCoordinateRegion adjustedRegion = [self.mapKit regionThatFits:viewRegion]; //add location to map
[self.mapKit setRegion:adjustedRegion animated:YES]; // create animation zooming
// Place Annotation Point
MKPointAnnotation *annotation1 = [[MKPointAnnotation alloc] init]; //Setting Sample location Annotation
[annotation1 setCoordinate:CLLocationCoordinate2DMake(51.900708, -2.083160)]; //Add cordinates
[self.mapKit addAnnotation:annotation1];
}
Now add that custom View to the ViewController.xib.
Now create this delegate method as below.
#pragma mark : MKMapKit Delegate
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id <MKAnnotation>)annotation
{
AnnotationView *pinView = nil; //create MKAnnotationView Property
static NSString *defaultPinID = #"com.invasivecode.pin"; //Get the ID to change the pin
pinView = (AnnotationView *)[self.mapKit dequeueReusableAnnotationViewWithIdentifier:defaultPinID]; //Setting custom MKAnnotationView to the ID
if ( pinView == nil )
pinView = [[AnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID]; // init pinView with ID
[pinView addSubview:self.customView];
addSubview:self.customView.center = CGPointMake(self.customView.bounds.size.width*0.1f, -self.customView.bounds.size.height*0.5f);
pinView.image = [UIImage imageNamed:#"Pin"]; //Set the image to pinView
return pinView;
}
I also got this answer few months ago from someone posted on Stackoverflow. I modified it to my project as I want. Hope this will do your work.

Resizing MKCircle on MKMapView is flickering

I'm trying to solve a problem since few days and I've got no good results. I've got a point and a circle on MKMapView. I've got UISlider and want to change size of MKCircle. Size is changed but during resizing this circle flickers and blinks.
Here is my code:
#implementation ViewController {
Annotation *_annotation;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_annotation = [[Annotation alloc] init];
[_annotation setCoordinate: CLLocationCoordinate2DMake(0, 0)];
[self.mapView addAnnotation:_annotation];
[self.mapView setCenterCoordinate:_annotation.coordinate animated:YES];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(_annotation.coordinate, 1000, 1000);
[self.mapView setRegion:region];
[self _addCircleOnCurrentLocationWithRadius:_slider.value];
}
- (IBAction)onSliderChanged:(UISlider *)sender {
[self.mapView removeOverlays:self.mapView.overlays];
[self _addCircleOnCurrentLocationWithRadius:sender.value];
}
- (void)_addCircleOnCurrentLocationWithRadius:(CGFloat)radius {
MKCircle *circle = [MKCircle circleWithCenterCoordinate:_annotation.coordinate radius:radius];
[self.mapView addOverlay:circle level:MKOverlayLevelAboveRoads];
}
#pragma mark - MKMapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKPinAnnotationView *view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"Annotation"];
return view;
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
MKCircleView *view = [[MKCircleView alloc] initWithCircle:overlay];
view.fillColor = [UIColor redColor];
view.strokeColor = [UIColor blueColor];
view.alpha = 0.3;
return view;
}
#end
I've tried:
removing old overlays and adding new one,
doing as above with NSOperationQueue
Here is a screen recording how it looks like.
I see that is possible to do, Apple did this in Reminders app.
Also I'm familiar with following topics:
Smooth resizing of MKCircle
Moving MKCircle on MKMapview and dragging MKMapview
Smooth resizing of MKCircle
Thank you in advance.
Edit
I've did it. I will add an answer with class which supports resizing today or tomorrow.
I've did it by creating subclass of MKCircleView and override - (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)ctx method. TSCircleView class is shared on github here..

Bringing a MKPolygon to the front

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.

How to: Display MKOverlay on MKMapView

I need to display an MKOverlay on a map, but can't get it to actually appear.
I'm following the example from Apple's Location Awareness Programming Guide and the overlay wont display. Any help would be greatly appreciated, this is the first iPhone app I've made, so I might be missing something simple.
NavViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface NavViewController : UIViewController <MKMapViewDelegate> {
}
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#end
NavViewController.m
#import "MSUNavViewController.h"
#import <CoreLocation/CoreLocation.h>
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView* aView = [[MKPolygonView alloc] initWithPolygon:(MKPolygon*)overlay];
aView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
aView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
aView.lineWidth = 3;
return aView;
}
return nil;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Define an overlay that covers Colorado.
CLLocationCoordinate2D points[4];
points[0] = CLLocationCoordinate2DMake(41.000512, -109.050116);
points[1] = CLLocationCoordinate2DMake(41.002371, -102.052066);
points[2] = CLLocationCoordinate2DMake(36.993076, -102.041981);
points[3] = CLLocationCoordinate2DMake(36.99892, -109.045267);
MKPolygon* poly = [MKPolygon polygonWithCoordinates:points count:4];
poly.title = #"Colorado";
[_mapView addOverlay:poly];
}
Storyboard:
Any coding suggestions would be greatly appreciated.
Thanks!
Make sure to set mapView's delegate to the view controller instance( perhaps File's owner in this case).
In interface builder, right-click on the map view, drag from hollowed circle at the right of delegate, to the File's Owner icon at the Placeholder section in the pane on the left.
For storyboard, connect to the View Controller icon instead of File's Owner.
Probably you killing aView in - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay by return nil;
Try to add else before.
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView* aView = [[MKPolygonView alloc] initWithPolygon:(MKPolygon*)overlay];
aView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
aView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
aView.lineWidth = 3;
return aView;
}
else return nil;
}
Did you solve your problem? Just drag from the circle beside delegate (in your screen shot) to the circle which is the name of the class.. there is a setting in the inspector that would allow you to display where you are also..? You made need to centre the map in say viewDidLoad so it displays over Colorado..

MKAnnotationView not showing up on the map

I am trying to annotate my map with MKPointAnnotation, like this:
- (void)viewDidLoad
{
NSLog(#"RootViewController viewDidLoad");
[super viewDidLoad];
CLLocationCoordinate2D coord =
CLLocationCoordinate2DMake(54.903683,23.895435);
MKPointAnnotation *toyAnnotation = [[MKPointAnnotation alloc] init];
toyAnnotation.coordinate = coord;
toyAnnotation.title = #"Title";
toyAnnotation.subtitle = #"Subtitle";
[mapView addAnnotation:toyAnnotation];
[toyAnnotation release];
}
- (MKAnnotationView *)mapView:(MKMapView *)m
viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"RootViewController mapView: viewForAnnotation:");
NSLog(#"%#",annotation);
MKAnnotationView *pin = [[MKAnnotationView alloc]
initWithAnnotation:annotation
reuseIdentifier:nil];
pin.enabled = YES;
pin.canShowCallout = YES;
return [pin autorelease];
}
Pin fails to appear on the map. RootViewController is a delegate for mapView and thus mapView:viewForAnnotation: method gets called:
2011-11-24 15:04:03.808 App[2532:707] RootViewController mapView: viewForAnnotation:
2011-11-24 15:04:03.810 App[2532:707] <MKPointAnnotation: 0x1885c0>
What am I doing wrong and how to fix this issue?
In viewForAnnotation, create an MKPinAnnotationView instead of an MKAnnotationView (for which you have to set the image).
Although for a default pin annotation view, you don't need to implement the delegate method at all.

Resources