I have a MKMapView that is having a very strange problem which is kind of hard to explain.
I have an MKOverlay that is saved between sessions by saving the points of the overlay to a file and loading the overlay in viewDidLoad. So here's the problem:
Steps
I add an MKOverlay to the MKMapView
On the second or third run after adding the MKOverlay to the map
everything on the map gets overlayed by an overlay of the same color
as the one I added. Everything on the map with the exception of the
view of the map that you can see when the view loaded.
Other weird things:
This problem only occurs when mapView.showsUserLocation is set to
true
This problem only occurs on iOS5. Everything works fine on iOS6.
If you zoom out the colored overlay re sizes the visible portion of
the map to fill the map at the new level at which you zoomed out to.
This guy is having the same problem, although his solution did not work for me.
Trouble with overlay using MKPolyline & MKPolylineView
Here is some of my code:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKPinAnnotationView *pinView = nil; //switch this to MKAnnotationView and use .image property if pins need to be images
if(annotation == self.startingPin || annotation == self.endingPin){
static NSString *defaultPinID = #"com.MagnusDevelopment.pin";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if (pinView == nil)pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:defaultPinID];
pinView.canShowCallout = YES;
pinView.animatesDrop = TRUE;
if(annotation == self.startingPin){
//starting pin
pinView.pinColor = MKPinAnnotationColorGreen;
}else{
//ending pin
pinView.pinColor = MKPinAnnotationColorRed;
}
}
return pinView;
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKPolylineView *aView = [[MKPolylineView alloc] initWithPolyline:(MKPolyline *)overlay];
aView.lineWidth = 7.0;
aView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.6];
return aView;
}
Code called when the view loads, I have logged the coordinates that are being added to the polyline array right before they are added, and everything is fine and dandy!
NSInteger numberOfSteps = pointsArray.count;
NSLog(#"number of steps: %i",numberOfSteps);
CLLocationCoordinate2D coordinates[numberOfSteps];
for (NSInteger index = 0; index < numberOfSteps; index++) {
CLLocation *location = [pointsArray objectAtIndex:index];
CLLocationCoordinate2D coordinate = location.coordinate;
coordinates[index] = coordinate;
}
MKPolyline *polyLine = [MKPolyline polylineWithCoordinates:coordinates count:numberOfSteps];
[self.mainMap addOverlay:polyLine];
Here is a picture of what happens when the overlay appears and the user pans to the side on the map:
Any ideas about what the problem is?
You're using dequeueReusableAnnotationViewWithIdentifier, but you're only assigning an annotation to pinView if the result of dequeueReusableAnnotationViewWithIdentifier was nil.
As that method will often return an existing MKPinAnnotationView instance, this should instead be:
MKPinAnnotationView *pin = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if (pin == nil) {
pin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID];
} else {
pin.annotation = annotation;
}
This might be the problem: When the mapview tries to access the view for user's current location, you are passing your customized view.
Try adding this with your existing implementation.
- (MKAnnotationView *) mapView:(MKMapView *) mapView viewForAnnotation:(id ) annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
return yourView;
}
PS: This may not be an issue of overlay view.
Best Regards.
Related
I'm implementing a custom pin for a map, the image is in te project's folder but didn't show.
Perhaps (and very likely) I'm doing something wrong.
The map displays and shows the pins (default red pins) but not the custom image nor the UIButtonTypeDetailDisclosure
This is the code I'm using:
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id<MKAnnotation>)annotation
{
// in case it's the user location, we already have an annotation, so just return nil
if ([annotation isKindOfClass:[MKUserLocation class]])
{
return nil;
}
static NSString *TheAnnotationIdentifier = #"theAnnotationIdentifier";
MKAnnotationView *shoppeAnnotationView =
[self.mapView dequeueReusableAnnotationViewWithIdentifier:TheAnnotationIdentifier];
if (shoppeAnnotationView == nil)
{
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:TheAnnotationIdentifier];
annotationView.canShowCallout = YES;
annotationView.image = [UIImage imageNamed:#"pin.png"];
annotationView.rightCalloutAccessoryView = [ UIButton buttonWithType:UIButtonTypeDetailDisclosure ];
annotationView.opaque = NO;
return annotationView;
}
return nil;
}
Any hints?
Thank you!
My MKMapView shows my position at startup but then the image never 'follows' me. The location gets updated and the screen does follow me, but the original "User Location" image stays behind.
Here is some code snippets:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
static NSString* AnnotationIdentifier = #"Annotation";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if(!pinView)
{
MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
if(annotation == mapView.userLocation) customPinView.image = [self rotate:[UIImage imageNamed:#"myCar.png"] orientation:UIImageOrientationUp];
else customPinView.image = [UIImage imageNamed:#"randomPin.png"];
customPinView.animatesDrop = NO;
customPinView.canShowCallout = YES;
return customPinView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
-(void)locationUpdate:(CLLocation *)location
{
CLLocationCoordinate2D loc = [location coordinate];
if(isFollowing)
[myMapView setCenterCoordinate:loc];//Works
}
and in my viewDidLoad I do call: [myMapView setShowsUserLocation:YES]; which does work.
So basically somewhere I neglect updating my position or its most possibly where I draw the new image for my current position.
Can anyone possibly see what I am missing or doing wrong there for it to not follow my location updates?
Thanks.
It's not clear if this is the issue but the viewForAnnotation method doesn't look right.
The annotation image is only being set when an annotation view is created. If a view is re-used, the annotation property is updated but not the image. It's possible that the re-used view is for an annotation of a different type requiring a different image.
The method should look something like this:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
static NSString* AnnotationIdentifier = #"Annotation";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if (!pinView)
{
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
pinView.animatesDrop = NO;
pinView.canShowCallout = YES;
}
else
{
pinView.annotation = annotation;
}
if (annotation == mapView.userLocation)
pinView.image = [self rotate:[UIImage imageNamed:#"myCar.png"] orientation:UIImageOrientationUp];
else
pinView.image = [UIImage imageNamed:#"randomPin.png"];
return pinView;
}
I am trying to implement a draggable "pin" (actually a custom icon) in a map view. This is the delegate code that I have:
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKAnnotationView *aView;
aView=(MKAnnotationView *) [mvMap dequeueReusableAnnotationViewWithIdentifier:annotation.title];
if (aView==nil)
aView=[[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotation.title] autorelease];
else
aView.annotation=annotation;
[aView setImage:[UIImage imageNamed:selIcon]];
aView.canShowCallout=TRUE;
[aView setDraggable:YES];
return aView;
}
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
MKAnnotationView *aV;
for (aV in views) {
CGRect endFrame = aV.frame;
int xDelta=0;
xDelta=sIcons.selectedSegmentIndex*61+22;
aV.frame = CGRectMake(aV.frame.origin.x-145+xDelta, aV.frame.origin.y - 150.0, aV.frame.size.width, aV.frame.size.height);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.7];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[aV setFrame:endFrame];
[UIView commitAnimations];
}
}
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState {
if (oldState == MKAnnotationViewDragStateDragging) {
addAnnotation *annotation = (addAnnotation *)view.annotation;
annotation.subtitle = [NSString stringWithFormat:#"%f %f", annotation.coordinate.latitude, annotation.coordinate.longitude];
}
if (newState == MKAnnotationViewDragStateEnding) {
CLLocationCoordinate2D droppedAt = view.annotation.coordinate;
NSLog(#"dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
}
}
The problem is that didChangeDragState is never being called (I've set a breakpoint in the routine to be sure). Everything else is working fine. My icons animate into the view, etc. When I tap the icon and hold my finger down the icon stays in place (the map doesn't move either which makes me think that I've actually hit the icon). Am I missing some kind of initialization?
Got it! The problem was in the interface file of my custom annotation class. The offending line read:
#property (nonatomic,readonly) CLLocationCoordinate2D coordinate;
It has to read:
#property (nonatomic,readwrite,assign) CLLocationCoordinate2D coordinate;
I guess that it has to have read/write capability.
Use MKPinAnnotationView instead of MKAnnotationView.
MKPinAnnotationView *pin = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"purple_pin"];
if (pin==nil)
{
pin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"purple_pin"];} else {
pin.annotation=annotation;
}
pin.pinColor = MKPinAnnotationColorPurple;
pin.draggable = TRUE;
return pin;
This is a tricky one! I've just implemented something similar (though I subclassed MKAnnotationView) and when I tried adding the annotationView:didChange delegate method to my view controller it didn't get called even though I was able to drag the annotation view??
I also copy/pasted your code into my view controller and it worked straight out of the box, with the delegate method being called and all!
The only thing I can think of is that instead of passing mvMap to dequeueReusableAnnotationViewWithIdentifier: try passing the mapView object that is supplied by the delegate method. Based on the code you provided above I am unable to tell if they are the same object so it might be worth a shot?
aView=(MKAnnotationView *) [mvMap dequeueReusableAnnotationViewWithIdentifier:annotation.title];
[EDIT TO ADD MY CODE AS REFERENCE]
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString* ParkAnnotationIdentifier = #"ParkAnnotationIdentifier";
MKAnnotationView* parkAnnotationView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ParkAnnotationIdentifier];
if (!parkAnnotationView)
{
MKAnnotationView *annotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:ParkAnnotationIdentifier] autorelease];
UIImage *imageIcon = [UIImage imageNamed:#"scooterIcon.png"];
annotationView.image = imageIcon;
annotationView.draggable = YES;
return annotationView;
}
else
{
parkAnnotationView.annotation = annotation;
}
return parkAnnotationView;
}
if you are using a subview of MKPinAnnotationView and are overriding setSelected(), make sure you call super.setSelected()
i have got the Latitude and Longitude from XML Parsing how can i show MapAnnotation(pin) on the Map,Please help me....
Create a new class that implements the MKAnnotation protocol. This will hold the lat/long, and will also have a title and description which will be displayed if you select the annotation after it has been rendered on your map.
The controller for the view that will display the map will need to implement the - - (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation; method of the MKMapViewDelegate protocol. The code in this method will look something like the code at the bottom (apologies for the poor formatting, I couldn't get right in here or at the bottom).
Then at some point in your controller code you will need to call something along the lines of [self.mapView addAnnotation: annotation]; where annotation is an instance of your custom annotation class created in step 1, with the lat/long set etc.
Finally, so that the viewForAnnotation method gets called correctly, and is something that is easy to miss, in interface builder, make sure that you set the delegate outlet of the MKMapView to be your controller (that implements the MKMapViewDelegate protocol.
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation {
static NSString *AnnotationViewIdentifier = #"annotationViewIdentifier";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier: AnnotationViewIdentifier];
if (annotationView == nil) {
annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier: AnnotationViewIdentifier] autorelease];
// This is all optional and depends on your requirements
annotationView.animatesDrop = NO;
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.enabled = YES;
annotationView.pinColor = MKPinAnnotationColorGreen;
}
return annotationView;
}
First you want to create MKAnnotation Protocol class (Here MyAnnotation class implements the MKAnnotation Protocol).This class should be includes lat, long, title, subtitle and also whatever you want.
Second, In your view controller, where you want to display the pin and you will implement this code,
AnnObj = [[MyAnnotation alloc] init];
AnnObj.latitude = [[latitude objectAtIndex:storyIndex] floatValue];
AnnObj.longitude = [[longitude objectAtIndex:storyIndex] floatValue];
MKCoordinateRegion region;
region.center = location;
MKCoordinateSpan span;
region.center.latitude = [[latitude objectAtIndex:storyIndex] floatValue];
region.center.longitude = [[longitude objectAtIndex:storyIndex] floatValue];
span.latitudeDelta = 0.0005;
span.longitudeDelta = 0.0005;
region.span = span;
[mapview setRegion:region animated:TRUE];
AnnObj.title = titleString;
AnnObj.subTitle = subTitleString;
NSString * titleString = [[buildingNames objectAtIndex:storyIndex] stringByReplacingOccurrencesOfString:#"\n\t" withString:#""];
[eventPoints addObject:AnnObj];
[mapview addAnnotations:eventPoints];
Third, Implement the MKAnnotation delegate method,
- (MKAnnotationView *)mapView: (MKMapView *)lmapView viewForAnnotation:(id <MKAnnotation>) annotation {
if (lmapView.userLocation == annotation){
return nil;
}
MKAnnotationView* myCusAnn = (MKAnnotationView*)[lmapView dequeueReusableAnnotationViewWithIdentifier:#"eventview"];
if(myCusAnn == nil)
{
myCusAnn = [[[ MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"eventview"] autorelease];
}
myCusAnn.canShowCallout = YES;
[myCusAnn setEnabled:YES];
return myCusAnn;
}
I hope it will help you.
I have a number of annotations on my map, in addition to the users current location. This works fine, except the default color for the users current location is the same as all of the other annotations. I'd like to make the pin green for the users current location so that it's uniquely identifiable from the other pins. How do I do this?
Bellow is the method I've been using (I can't find a way to determine which annotation is the users current location):
- (MKAnnotationView *)mapView:(MKMapView *)mapViewLocal viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"Pin";
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (pinView == nil)
{
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier] autorelease];
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
If you use standard MKUserLocation type for user location then you can check annotation's type if it is the same:
...
if ([annotation isKindOfClass:[MKUserLocation class]]){
// This is annotation for user location
}