Why a MKPolyline doesn't show on the MKMapView? - iphone

I'm pretty new on programmin to develop iphone applications and i would like to know why the MKPolyline that I create with 2 MKMapPoints doesn't appears in the MKMapView that I insert on my view.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
map = [[MKMapView alloc] initWithFrame:self.view.bounds];
MKMapPoint * pointsArray = malloc(sizeof(CLLocationCoordinate2D)*4);
CLLocationCoordinate2D punto1;
punto1.latitude =39.468502;
punto1.longitude =-0.398469;
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc]init];
annotationPoint.coordinate = punto1;
annotationPoint.title = #"Point 1";
MKPointAnnotation *annotationPoint2 = [[MKPointAnnotation alloc]init];
annotationPoint2.coordinate = CLLocationCoordinate2DMake(39.472312,-0.386453);
annotationPoint2.title = #"Point 2";
[map addAnnotation:annotationPoint];
[map addAnnotation:annotationPoint2];
pointsArray[0]= MKMapPointForCoordinate(punto1);
pointsArray[1]= MKMapPointForCoordinate(CLLocationCoordinate2DMake(39.467011,-0.390015));
pointsArray[2]= MKMapPointForCoordinate(CLLocationCoordinate2DMake(39.469926,-0.392118));
pointsArray[3]= MKMapPointForCoordinate(CLLocationCoordinate2DMake(39.472312,-0.386453));
routeLine = [MKPolyline polylineWithPoints:pointsArray count:4];
free(pointsArray);
[map addOverlay:routeLine];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2DMake(39.467011,-0.392515), 1100, 1100);
[map setRegion:region];
[self.view insertSubview:map atIndex:0];
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id )overlay
{
MKOverlayView* overlayView = nil;
MKPolylineView * routeLineView = [[MKPolylineView alloc] initWithPolyline:self.routeLine];
routeLineView.fillColor = [UIColor blueColor];
routeLineView.strokeColor = [UIColor orangeColor];
routeLineView.lineWidth = 3;
overlayView = routeLineView;
return overlayView;
}
The annotations are OK and they show correctly on the map.
Hope someone can help, thanks!!!

The MKPolyline doesn't show because the map's delegate is not set.
The viewForOverlay delegate method won't get called if the map's delegate property is not set. Because the delegate method is never called, the MKPolylineView never gets created, etc...
After creating the MKMapView, set its delegate:
map = [[MKMapView alloc] initWithFrame:self.view.bounds];
map.delegate = self; // <-- add this
I'd like to mention a few other unrelated points:
Since you are putting MKMapPoint values in the pointsArray, the malloc should use sizeof(MKMapPoint) instead of sizeof(CLLocationCoordinate2D). It happens to work with the wrong code because both structs happen to be the same size. But you should still use the right code.
MKPolyline also has a polylineWithCoordinates:count: method so you can pass CLLocationCoordinate2D instead of MKMapPoint structs. This can be easier to read, understand, and avoids having to convert from coordinates to mappoints.
The MKPolylineView uses strokeColor only so setting the fillColor for it does nothing.
In the viewForOverlay delegate method, it's much better practice to use the overlay parameter that is passed into the method instead of the externally declared routeLine. This will be extremely important if you want to add more than one overlay. You would also first check what kind of class overlay is and then create the appropriate view for it.

Related

MKPointAnnotation unable to coordinate

In my mapView application i am trying to make the annotation, Without adding annotation i am getting the mapView but when i try to add annotation, Only the annotation mark is visible the map becomes invisible.
Before adding Annotation:
Code:
[super viewDidLoad];
mapView = [[MKMapView alloc]initWithFrame:CGRectMake(0, 0, 320, elf.view.frame.size.height)];
mapView.showsUserLocation = YES;
mapView.mapType = MKMapTypeStandard;
mapView.delegate = self;
[self.view addSubview:mapView];
Image:
After Adding Annotation:
Code:
[super viewDidLoad];
mapView = [[MKMapView alloc]initWithFrame:CGRectMake(0, 0, 320, self.view.frame.size.height)];
mapView.showsUserLocation = YES;
mapView.mapType = MKMapTypeStandard;
mapView.delegate = self;
[self.view addSubview:mapView];
MKUserLocation *userLocation = mapView.userLocation;
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 20, 20);
[mapView setRegion:region];
MKPointAnnotation *annotation = [[MKPointAnnotation alloc]init];
annotation.coordinate = userLocation.location.coordinate;
annotation.title = #"Here you r";
annotation.subtitle = #"Pondy";
[mapView addAnnotation:annotation];
Image:
I need the annotation appear in the map. Thanks in advance.
Try changing the value of MKCoordinateRegionMakeWithDistance.
it add anotation of your current location...You put span 20, 20. That is very close so only it takes some time to load
mapView1.showsUserLocation = YES;
MKUserLocation *userLocation = mapView1.userLocation;
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 200, 200);
[mapView1 setRegion:region];
MKPointAnnotation *annotation = [[MKPointAnnotation alloc]init];
annotation.coordinate = userLocation.location.coordinate;
annotation.title = #"Title";
annotation.subtitle = #"Sub Title";
[mapView1 addAnnotation:annotation];
I hope it will be work for you
Blue screen usually means your current location is in the sea as discussed above.
Please check the current location which you are getting is correct or not.
Usually for showing current location in MKMap we write map.showuserlocation=YES and it shows the bluew dot in the map view if you have to change the annotation pin in your map of the currentlocation you can go in view for annotation methiod and check for the userlocation class and hence change the pin of your userlocation annotation.
I think that at the time you add your annotation, the user location is still unknown:
MKUserLocation *userLocation = mapView.userLocation;
// At that time maybe the userLocation hasn't been retrieved through the GPS yet
...
MKPointAnnotation *annotation = [[MKPointAnnotation alloc]init];
annotation.coordinate = userLocation.location.coordinate;
annotation.title = #"Here you r";
annotation.subtitle = #"Pondy";
[mapView addAnnotation:annotation];
In that case, the annotation position will be 0, 0 and as your map region is also based on the user location from the mapView, you'll end in the middle of the sea at position 0,0.
In order to monitor changes of the user location of the mapView, and then readjust your pin and / or the map region only when you're sure that the userLocation given by the mapView is correct, just implement the MKMapViewDelegate protocol for this selector :
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
then, in the body of your implementation, do what you did previously, but now, you can check if the user location is correct or not, and only adjust your map region and annotation once you're sure that the user location is OK.
The reason is location of your simulator is set to NONE.
Follow these steps, you will get your answer...
`
1) Open your simulator.
2) go to Debug menu
3) Select Location.
3) Select Custom Location. (OR Select location of Apple to skip step
No 4.)
4) Enter your Latitude and Longitude.
5) Delete your app from simulator.
6) Run your project again.
`
Enjoy....

Free hand Draw polyline overlay in ios

I'm trying to trace the route by free hand on a MKMapView using overlays (MKOverlay).
Each time when we move the finger i extend the polyline with last coordinate with new coordinate,all are working fine except when extending polyline overlay the whole overlay is blinking in device(only sometimes),so i can,t trace the problem.
The code i have tried is given below.
- (void)viewDidLoad
{
j=0;
coords1 = malloc(2* sizeof(CLLocationCoordinate2D));
coordinatearray=[[NSMutableArray alloc]init];
UIPanGestureRecognizer *GestureRecogonized = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(gestureDetacted:)];
[self.myMapView addGestureRecognizer:GestureRecogonized];
}
- (void)gestureDetacted:(UIPanGestureRecognizer *)recognizer
{
if(UIGestureRecognizerStateBegan==recognizer.state)
{
CGPoint point = [recognizer locationInView:self.myMapView];
CLLocationCoordinate2D tapPoint = [self.myMapView convertPoint:point toCoordinateFromView:self.view];
CLLocation *curLocation = [[CLLocation alloc] initWithLatitude:tapPoint.latitude longitude:tapPoint.longitude];
[coordinatearray addObject:curLocation];
}
coords1[0]=[[coordinatearray objectAtIndex:j] coordinate];
if(UIGestureRecognizerStateChanged==recognizer.state)
{
j++;
CGPoint point = [recognizer locationInView:self.myMapView];
CLLocationCoordinate2D tapPoint = [self.myMapView convertPoint:point toCoordinateFromView:self.view];
CLLocation *curLocation = [[CLLocation alloc] initWithLatitude:tapPoint.latitude longitude:tapPoint.longitude];
[coordinatearray addObject:curLocation];
coords1[1]=CLLocationCoordinate2DMake(tapPoint.latitude,tapPoint.longitude);
polyLine = [MKPolyline polylineWithCoordinates:coords1 count:2];
[self.myMapView addOverlay:polyLine];
}
}
in overlay delegate
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
if([overlay isKindOfClass:[MKPolyline class]]){
MKPolylineView *polylineView = [[MKPolylineView alloc] initWithPolyline:overlay];
polylineView.strokeColor = [UIColor orangeColor];
polylineView.lineWidth = 20;
polylineView.fillColor=[[UIColor orangeColor] colorWithAlphaComponent:.1];
return polylineView;
}
}
can anybody know why that flickering or blinking effect is coming and how to remove it.
Thanks in advance.
Rather than adding hundreds of really small views (which is really computationally intensive), i would rather remove the polyline overlay and add a new one with all the points on the map in it at each change in the pan recognizer (for a smoother effect, you can first add the new one and then remove the old one). Use your coordinateArray to create the MKPolyline overlay that contains all of the points, rather than the last 2 points.
You could do something like:
[coordinatearray addObject:curLocation];;
CLLocationCoordinate2D* coordArray = malloc(sizeof(CLLocationCoordinate2D)*[coordinatearray count]);
memcpy(coordArray, self.coordinates, sizeof(CLLocationCoordinate2D)*([coordinatearray count]-1));
coordArray[[coordinatearray count]-1] = curLocation;
free(self.coordinates);
self.coordinates = coordArray;
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coordArray count:[coordinatearray count]];
MKPolyline *old = [[self.mapView overlays] lastObject];
[self.mapView addOverlay:polyline];
[self.mapView removeOverlay:old];
Where self.coordinate is a property of type CLLocationCoordinate2D*, this way you can memcpy the existing array into a new one (memcpy is really efficent) and only need to add the last point to the array, rather than having to loop through each point of the NSArray *coordinatearray.
You also have to change your if(UIGestureRecognizerStateBegan==recognizer.state) part, to add in self.coordinates the first tapped point.
Just something like:
self.coordinates = malloc(sizeof(CLLocationCoordinate2D));
self.coordinates[0] = curLocation;
EDIT: I think the problem is that map overlays draw themselves for different discrete values of zoom levels. At a certain zoom level it would appear that the overlay first draws itself at the bigger zoom level, and then at the smaller zoom level (in fact drawing over the previously drawn overlay). When you try to show an animation such as that from drawing the user panning gesture at this zoom level, that is why the overlay keeps flickering. A possible solution would be to use a transparent view that you put on top of the map view, where the drawing is performed while the user keeps moving the finger. Only once the panning gesture ends you then create a map overlay that you "pin" to the map and you clean the drawing view. You also need to be careful when redrawing the view, as you should each time specify only the rect to be redrawn and then redraw only in that rect, as redrawing the whole thing each time would cause a flicker in this view as well. This is definitely much more coding involved, but it should work. Check this question for how to incrementally draw on a view.

Showing Specific Region Using MapKit

I want to know is it possible to show only specific region on map not the full world map using Map Kit.
Like if i want to show Asia map in my application then map kit hides remaining part of the map.
To handle the "map kit hides remaining part of the map" requirement, one thing you can do is create a black polygon overlay that covers the whole world with a cutout over Asia (or wherever you like).
For example, where you initialize the map (eg. in viewDidLoad):
CLLocationCoordinate2D asiaCoords[4]
= { {55,60}, {55,150}, {0,150}, {0,60} };
//change or add coordinates (and update count below) as needed
self.asiaOverlay = [MKPolygon polygonWithCoordinates:asiaCoords count:4];
CLLocationCoordinate2D worldCoords[4]
= { {90,-180}, {90,180}, {-90,180}, {-90,-180} };
MKPolygon *worldOverlay
= [MKPolygon polygonWithCoordinates:worldCoords
count:4
interiorPolygons:[NSArray arrayWithObject:asiaOverlay]];
//the array can have more than one "cutout" if needed
[myMapView addOverlay:worldOverlay];
and implement the viewForOverlay delegate method:
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView *pv = [[[MKPolygonView alloc] initWithPolygon:overlay] autorelease];
pv.fillColor = [UIColor blackColor];
pv.alpha = 1.0;
return pv;
}
return nil;
}
This looks like this:
If you also want to restrict the user from scrolling beyond Asia or zooming too far out, then you'll need to do that manually as well. One possible way is described in Restrict MKMapView scrolling. Replace theOverlay in that answer with asiaOverlay.
You can specify the region as an MKCoordinateRegion and then tell an MKMapView instance to only show that region using the setRegion and regionThatFits message.
Alternatively you could use the visibleMapRect property instead of the region. This might better fit your needs.
In short read the MKMapView Class Reference document from Apple.
Lifting from some code I've done in the past that assumes a mapView and a given location called locationToShow I used an MKCoordinateRegion.
- (void) goToLocation {
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta=0.01;
span.longitudeDelta=0.01;
region.span=span;
region.center=locationToShow;
[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];
}

MKOverlay not resizing smoothly

I have added a MKCircle as MKOverlay to my MKMapView. Also I added an UISlider to decide the radius of the circle. Unfortunately when using this it seems a bit "laggy", not smootly like I want it to be.
Example:
http://dl.dropbox.com/u/3077127/mkoverlayDelay.mov
This is my code:
- (void)addCircle
{
// draw the radius circle for the marker
double radius = 2000.0;
MKCircle *circle = [MKCircle circleWithCenterCoordinate:location radius:radius];
[circle setTitle:#"background"];
[mapView addOverlay:circle];
MKCircle *circleLine = [MKCircle circleWithCenterCoordinate:location radius:radius];
[circleLine setTitle:#"line"];
[mapView addOverlay:circleLine];
}
- (void)addCircleWithRadius:(double)radius
{
MKCircle *circle = [MKCircle circleWithCenterCoordinate:location radius:radius];
[circle setTitle:#"background"];
[mapView addOverlay:circle];
MKCircle *circleLine = [MKCircle circleWithCenterCoordinate:location radius:radius];
[circleLine setTitle:#"line"];
[mapView addOverlay:circleLine];
}
- (void)sliderChanged:(UISlider*)sender
{
[mapView removeOverlays:[mapView overlays]];
double radius = (sender.value * 100);
[self addCircleWithRadius:radius];
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay{
MKCircle *circle = overlay;
MKCircleView *circleView = [[[MKCircleView alloc] initWithCircle:overlay] autorelease];
if ([circle.title isEqualToString:#"background"])
{
circleView.fillColor = UIColorFromRGB(0x598DD3);
circleView.alpha = 0.25;
}
else
{
circleView.strokeColor = UIColorFromRGB(0x5C8AC7);
circleView.lineWidth = 2.0;
}
return circleView;
}
Does anybody have any suggestions on how I can smoothen this?
Best regards,
Paul Peelen
I have tried your code and found a very easy way to make it smoother.
If you change the order of the calls in: - (void)sliderChanged:(UISlider*)sender
You can call [self addCircleWithRadius:radius];
before calling [mapView removeOverlays:[mapView overlays]];
Just make sure you dont remove the overlays you just added, only the old ones.
This will give you a smoother resizing, specially when the new circle is smaller than the old one.
For circles that are bigger you are probably better off using NSOperations to ensure the views are created faster, this will make it smoother.
Hope this helps.
Your best bet might be to draw the circle yourself, either using another UIView on top of the MKMapView, or using a MKAnnotationView within the Map View.
This blogger has done a similar thing (before iOS4 when overlays were added).
http://spitzkoff.com/craig/?p=65

Adding subview to MKMapView that's above the map but below the annotation views?

For an app I'm building, I have a custom "old map" effect PNG that I'd like to overlay on the MKMapView view. Unfortunately, neither way I've tried to do this is exactly right:
Add a subview to the MKMapView (issue: the UIImageView for the PNG gets placed above the Annotation pins, which looks weird
Make the image an annotation view itself and place it below the other ones (issue: on scroll, I have to move the annotation view image, and for a while there's just the regular map instead of the old-timey-effect).
What I basically want to accomplish is having a subview that is layered above the maptiles but below the annotation views, and that will hold steady while the user scrolls around—any thoughts?
Note that all MKMapView subviews hierarchy, annotations, behaviour etc proceeds in internal private MKMapViewInternal class so changing anything (in a way I suggest or another) in this structure may cause your application to be rejected from Appstore.
I do not have a full solution, just an idea. My idea is to make an overlay view have the same superview as annotation views and ensure that annotations will be placed over our overlay. In MKMapViewDelegate we implement:
- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views{
if (views.count > 0){
UIView* tView = [views objectAtIndex:0];
UIView* parView = [tView superview];
UIView* overlay = [tView viewWithTag:2000];
if (overlay == nil){
overlay = [[UIImageView alloc] initWithImage:[UIImage imageNamed:MY_IMAGE_NAME]];
overlay.frame = parView.frame; //frame equals to (0,0,16384,16384)
overlay.tag = 2000;
overlay.alpha = 0.7;
[parView addSubview:overlay];
[overlay release];
}
for (UIView* view in views)
[parView bringSubviewToFront:view];
}
}
This code does a half of what you want - you get a view placed between map tiles and annotations. The remaining task is to change custom overlay view frame according to map scrolling/zooming (I'll post if I find the way).
I did something similar and in a different way in the end. I wanted to fade the mapview out by adding a white overlay, but I didn't want to fade the annotations. I added an overlay to the map (actually I had to add two as it didn't like me trying to add an overlay for the entire globe).
CLLocationCoordinate2D pt1, pt2, pt3, pt4;
pt1 = CLLocationCoordinate2DMake(85, 0);
pt2 = CLLocationCoordinate2DMake(85, 180);
pt3 = CLLocationCoordinate2DMake(-85, 180);
pt4 = CLLocationCoordinate2DMake(-85, 0);
CLLocationCoordinate2D coords[] = {pt1, pt2, pt3, pt4};
MKPolygon *p = [MKPolygon polygonWithCoordinates:coords count:4];
[self.mapView addOverlay:p];
pt1 = CLLocationCoordinate2DMake(85, 0);
pt2 = CLLocationCoordinate2DMake(85, -180);
pt3 = CLLocationCoordinate2DMake(-85, -180);
pt4 = CLLocationCoordinate2DMake(-85, 0);
CLLocationCoordinate2D coords2[] = {pt1, pt2, pt3, pt4};
MKPolygon *p2 = [MKPolygon polygonWithCoordinates:coords2 count:4];
[self.mapView addOverlay:p2];
Then you need to add the map delegate to draw them:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:MKPolygon.class]) {
MKPolygonRenderer *polygonView = [[MKPolygonRenderer alloc] initWithOverlay:overlay];
polygonView.fillColor = [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:0.4];
return polygonView;
}
return nil;
}
It's pretty old, but maybe still relevant for some of you.
I wanted to make the map darker, but not the annotation views.
What i did was very simple and i don't yet know if it has some fallbacks...
I put a UIView with a black-transparent background above the MKMapView, and then added the following code:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray<MKAnnotationView *> *)views
{
for (MKAnnotationView *view in views) {
view.center = [self.mapView convertCoordinate:view.annotation.coordinate toPointToView:self.viewDark];
[self.viewDark addSubview:view];
}
}
Hope it helps.. and of course - if you find any issues about this solution please let me know :)
Edit #1:
Forgot to mention that self.viewDark should have userInteraction disabled.
Edit #2:
Another thing that helped me is setting self.viewDark's autoresizesSubviews to NO.