MapView Annotation not dragging - iphone

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()

Related

MKOverlay overlays everything with weird circumstances only on ios5?

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.

how to add custom callout view in mapview

I am new to mapkit in objective-c. I am able to add custom annotation in mapview.
i need to place custom callout view like below image
.
But i didn't get how can i design callout view like this.
i know i need to add callout in view for annotation method.
- (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:#"blue_without_pin.png"];
annotationView.annotation = annotation;
return annotationView;
}
Basically what you want to do is add a new custom view by implementing the following method in your MKMapViewDelegate instance :
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
CGPoint annotationCenter = [mapView convertCoordinate:view.annotation.coordinate toPointToView:mapView];
[self displayCustomCalloutForAnnotation:view.annotation center:annotationCenter];
}
#interface CustomAnnotaionView : MKAnnotationView
#property (strong, nonatomic) UIView *calloutView;
-(void)setSelected:(BOOL)selected animated:(BOOL)animated;
#end
#implementation CustomAnnotaionView
#synthesize calloutView = _calloutView;
-(void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if(selected)
{
[self.calloutView setFrame:CGRectMake(30, 130, 250, 135)];
[self.calloutView sizeToFit];
self.calloutView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"popup.png"]];
[self addSubview:self.calloutView];
}
else
{
[self.calloutView removeFromSuperview];
}
}
-(void)didAddSubview:(UIView *)subview
{
if([[[subview class]description]isEqualToString:#"UICalloutView"])
{
[subview removeFromSuperview];
}
}
Use this customAnnotationView class in MapView delegate method,
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id
<MKAnnotation>)annotation
{
if([annotation isKindOfClass:[MapAnnotation class]])
{
CustomAnnotationView *annotationView = (CustomAnnotaionView*)[theMapView dequeueReusableAnnotationViewWithIdentifier:#"CustomAnnotationView"];
if(!annotationView)
{
MapAnnotation *annotation = (MapAnnotation *)annotation;
annotationView = [[CustomAnnotaionView alloc]initWithAnnotation:annotation reuseIdentifier:#"CustomAnnotationView"];
[annotationView setCanShowCallout:YES];
}
else
annotationView.annotation = annotation;
return annotationView;
}
return nil;
}
One solution for this would be to detect the exact coordinate of the annotation selected relative to the parent UIView and then drawing a UIView directly on top of the MKMapView.
Here's a snippet that might help:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
CGPoint pointInMap = [self.mapView convertCoordinate:buildingAnnotation.coordinate
toPointToView:self.view];
// Hide the default callout generated by MKMapView
[mapView deselectAnnotation:view.annotation animated:NO];
// Create a custom callout view and center the arrow on the pointInMap CGPoint
}
You must implement a callout view yourself, and use
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
this delegate to make the callout view showing at right place,
IOS have not any method to custom callout view yet

show another view when map annotation are clicked

I have a map with only one annotation.I created a simple class which I want it to show when the user clicks on the annotation.The problem is that when I click on the annotation nothing happens.
Here is my code:
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark{
NSLog(#"Reverse Geocoder completed");
mPlacemark=placemark;
[mapView addAnnotation:placemark];
}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
annView.animatesDrop=TRUE;
//create UIButton for annotation
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
//NSInteger annotationValue = [self.annotations indexOfObject:annotation];
[detailButton addTarget:self action:#selector(showDetailView:) forControlEvents:UIControlEventTouchUpInside];
annView.rightCalloutAccessoryView=detailButton;
return annView;
}
-(void)showDetailView:(id)sender{
NSLog("inside the stupid method");
MyDetailViewController *detailView=[[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:nil];
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
My showDetailView function never gets called.Please help me.I'm new to iphone and I might forget a simple thing.Thanks
Still not working!!!!
First, check that the map view's delegate is set otherwise your viewForAnnotation method will not get called.
Next, the accessory button appears on the annotation's callout which will only appear if you set canShowCallout:
annView.canShowCallout = YES;
Next, instead of using your own method to handle the button action, it's much better to use the map view's own calloutAccessoryControlTapped delegate method which gets called when tapping an accessory:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"inside the stupid method");
//Here, the annotation tapped can be accessed using view.annotation
}
Remove the [detailButton addTarget... line from viewForAnnotation.
Also note your NSLog in the showDetailView method is missing the leading # which will result in a crash when the method does get called.
Another thing is you should use dequeueReusableAnnotationViewWithIdentifier in viewForAnnotation to enable annotation view re-use.
Example as requested:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
static NSString *annReuseId = #"currentloc";
MKPinAnnotationView *annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:annReuseId];
if (annView == nil)
{
annView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annReuseId];
annView.animatesDrop = YES;
annView.canShowCallout = YES;
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annView.rightCalloutAccessoryView=detailButton;
}
else {
annView.annotation = annotation;
}
return annView;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"calloutAccessoryControlTapped: annotation = %#", view.annotation);
MyDetailViewController *detailView=[[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:nil];
//here, can set annotation info in some property of detailView
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
Ok. If you are using storyboards, you need to create a segue between the map view controller and then where the below code exists:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"calloutAccessoryControlTapped: annotation = %#", view.annotation);
MyDetailViewController *detailView=[[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:nil];
//here, can set annotation info in some property of detailView
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
Use the following code instead:
NSLog(#"calloutAccessoryControlTapped: annotation = %#", view.annotation);
[self performSegueWithIdentifier:#"DetailViewController" sender:self];
This will give you the movement to the detailed view.
write your function like this:
-(IBAction)showDetailView:(id)sender{
NSLog("inside the stupid method");
MyDetailViewController *detailView=[[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:nil];
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
you have to set the map delegate to self like this,
mapview.delegate=self; (if your calling the method in same page)
if you don't assign the delegate in your code the respective methods won't be called, so check if you have the above mentioned line.
Replace -(IBAction)showDetailView:(UIView*)sender with following...
-(void)showDetailView:(id)sender

How to show the title on my Pin Annotation and make it clickable to perform some action?

In my MapView i have pin dropped on certain locations and now i want to display some title on these pin annotation and make it clickable so that on click i can push another view.
Please help !!!!
This is the code snippet:
Custom Annotation View using this method
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
if (annotation == mapView.userLocation) return nil;
MKPinAnnotationView *pin = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier: #"asdf"];
if (pin == nil)
{
pin = [[[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"asdf"] autorelease];
}
else
{
pin.annotation = annotation;
}
pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pin.pinColor = MKPinAnnotationColorRed;
pin.animatesDrop = YES;
[pin setEnabled:YES];
[pin setCanShowCallout:YES];
return pin;
}
Delegate method which get's called when you tap on the button
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
//DetailPg=[[BookMarksDetail alloc]initWithNibName:#"BookMarksDetail" bundle:nil];
//DetailPg.selectRest =[view.annotation title]; //In addition, to get the Title of the AnnotationView.
//DetailPg.m=1;
//[self.navigationController pushViewController:DetailPg animated:YES];
//[DetailPg release];
}
The answers to this are all provided in Apples Docs I suggest you read them more closely. However to have a call out all you need to do is set the title property.
For buttons and such you need to use the MapView Delegate method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
_pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
Apple suggests reusing annotations this is something you will need to take into account.

Getting wrong annotation from MapView Delegate method

I have declared the following in my .h file:
Annotation *annoForMoreDetails.
However when I try to set it to the current annotation in the method
- (MKAnnotationView *)mapView:(MKMapView *)mapViews viewForAnnotation:(id <MKAnnotation> )annotation
It gets set to the wrong object.
Here is my code:
- (MKAnnotationView *)mapView:(MKMapView *)mapViews viewForAnnotation:(id <MKAnnotation> )annotation
{
NSLog(#"welcome into the map view annotation");
// if it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
for (annotation in [mapView annotations]) {
if ([[mapView annotations] containsObject:annotation]) {
annoForMoreDetails = annotation;
}
}
if (annoForMoreDetails.coordinate.latitude != mapViews.userLocation.coordinate.latitude && annoForMoreDetails.coordinate.longitude != mapViews.userLocation.coordinate.longitude) {
// try to dequeue an existing pin view first
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView* pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
pinView.animatesDrop=YES;
pinView.canShowCallout=YES;
pinView.pinColor = MKPinAnnotationColorGreen;
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
[rightButton addTarget:self
action:#selector(moreDetails)
forControlEvents:UIControlEventTouchUpInside];
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
else if (annoForMoreDetails.coordinate.latitude == mapViews.userLocation.coordinate.latitude && annoForMoreDetails.coordinate.longitude == mapViews.userLocation.coordinate.longitude) {
return nil;
}
return nil;
}
- (void)moreDetails {
annotationCalloutView.frame = CGRectMake(70, 120, 188, 218);
annotationCalloutView.alpha = 0.0;
mapView.userInteractionEnabled = NO;
annotationCalloutView.userInteractionEnabled = YES;
titleLabel.text = annoForMoreDetails.title;
titleLabel.numberOfLines = 0;
titleLabel.userInteractionEnabled = NO;
addressLabel.text = annoForMoreDetails.subtitle;
[self.view addSubview:annotationCalloutView];
[UIView beginAnimations:#"callout" context:NULL];
[UIView setAnimationDuration:0.6];
annotationCalloutView.alpha = 1.0;
[UIView commitAnimations];
}
If you see anything wrong please point it out!
Are you trying to use annoForMoreDetails to know which annotation the user tapped on so you can show more details? If that's the case, there's an easier way. Use the map view's calloutAccessoryControlTapped delegate method. It will be called when the user taps on the accessory button and it will pass the annotation view which includes the annotation as a property.
Remove the annoForMoreDetails instance variable and in viewForAnnotation, remove the for-loop that sets annoForMoreDetails (I think it ends up setting it to the last annotation every time). Remove all other references to annoForMoreDetails.
Also in viewForAnnotation, remove the addTarget line on the rightButton since you'll replace your custom moreDetails method with an implementation of calloutAccessoryControlTapped:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
Annotation *annoForMoreDetails = (Annotation *)view.annotation;
//the code currently in moreDetails method goes here...
}