I am new to iOS programming, and I am having trouble with passing data between view controllers.
I have a view controller that geocodes an annotation on a map view, and sets the address as the title of the annotation view. The annotation view has a callout button that pushes another view to the stack, which has a label, and I want the address that was geocoded to show up as the label. Here is some code:
This is where I drop the pin:
-(void)press:(UILongPressGestureRecognizer *)recognizer
{
CGPoint touchPoint = [recognizer locationInView:worldView];
CLLocationCoordinate2D touchMapCoordinate = [worldView convertPoint:touchPoint toCoordinateFromView:worldView];
geocoder = [[CLGeocoder alloc]init];
CLLocation *location = [[CLLocation alloc]initWithCoordinate:touchMapCoordinate
altitude:CLLocationDistanceMax
horizontalAccuracy:kCLLocationAccuracyBest
verticalAccuracy:kCLLocationAccuracyBest
timestamp:[NSDate date]];
[geocoder reverseGeocodeLocation:location
completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"reverseGeocoder:completionHandler: called");
if (error) {
NSLog(#"Geocoder failed with error: %#", error);
}
if (placemarks && placemarks.count > 0)
{
CLPlacemark *place = [placemarks objectAtIndex:0];
_address = [NSString stringWithFormat:#"%# %#, %# %#", [place subThoroughfare], [place thoroughfare], [place locality], [place administrativeArea]];
if (UIGestureRecognizerStateBegan == [recognizer state]) {
_addressPin = [[MapPoint alloc]initWithCoordinate:touchMapCoordinate
title:_address];
[worldView addAnnotation:_addressPin];
}
}
}];
}
Here is the code for the view where I want the address to show up:
#import <UIKit/UIKit.h>
#class MapPoint;
#interface PinViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate,UITextFieldDelegate>
{
__weak IBOutlet UILabel *addressLabel;
}
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#property (strong, nonatomic) MapPoint *pin;
-(void)takePicture:(id)sender;
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
#end
#import "PinViewController.h"
#import "MapPoint.h"
#interface PinViewController ()
#end
#implementation PinViewController
#synthesize imageView, pin;
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[addressLabel setText:[pin title]];
}
The class MapPoint has a title property and subtitle property. When the PinViewController is pushed to the top of the stack, I try to set the text of the label to the title of the pin, but the label does not show any text. Can someone help me out and tell me what I am missing? Your help is greatly appreciated.
You must share more code blocks for this question :) . instead of you have to get more idea about Communication in Object-Oriented Programs
You can read a good tutorial about Passing Data Between View Classes here
Read best way to pass an object between two views here
There is a good document that i found in Developer.apple regarding CommunicatingWithObjects
Also you can watch a good video here
Just create property for the label and pass the label text from callout tapped method
- (void)mapView:(MKMapView *)mapView annotationView:(MKPinAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
Details *det=[[Details alloc]init];
det.label.text=annotation.title;
[self.navigationController pushViewController:det animated:YES];
[det release];
}
Related
I have a map annotation that I would like to push to a detail view controller when the calloutAccessoryControlTapped is touched. The problem I'm having is that all annotations all send the same data to the detail vc. I'm not sure if it's the indexPath that's incorrect. Each map annotation shows the correct info in the callout box, it's just that when the calloutAccessoryControlTapped is tapped it pushes the same info for the first listing in the array it seems.
It's the NSNumber *catListingMapId; in the annotation that I really need to access and pass to the detail vc (and there I download data based on that catListingMapId).
- (void)mapView:(MKMapView *)mv annotationView:(MKAnnotationView *)pin calloutAccessoryControlTapped:(UIControl *)control {
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
MyAnnotation *theAnnotation = (MyAnnotation *) pin.annotation;
NSLog(#"the Annotation %#",theAnnotation.catListingMapId);
detailViewController.listingId = theAnnotation.catListingMapId;
// detailViewController.listingId = [[self.listingNodesArray objectAtIndex:selectedIndexPath] objectForKey:#"id"];
NSNumber *catNumber = [NSNumber numberWithInt:[catID intValue]];
detailViewController.catId = catNumber;
NSLog(#"detailViewController.listingId %#",detailViewController.listingId );
[self.navigationController pushViewController:detailViewController animated:YES];
}
here's my myAnntoationMap file:
#interface MyAnnotation : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString* title;
NSString* subtitle;
NSNumber *latString;
NSNumber *lngString;
NSNumber *catMapId;
NSNumber *catListingMapId;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString* title;
#property (nonatomic, copy) NSString* subtitle;
#property (nonatomic,copy) NSNumber *latString;
#property (nonatomic,copy) NSNumber *lngString;
#property (nonatomic,copy) NSNumber *catMapId;
#property (nonatomic,copy) NSNumber *catListingMapId;
#end
and the .m
#import "MyAnnotation.h"
#implementation MyAnnotation
#synthesize title;
#synthesize subtitle;
#synthesize coordinate;
#synthesize latString,lngString;
#synthesize catMapId;
#synthesize catListingMapId;
- (void)dealloc
{
[super dealloc];
self.title = nil;
self.subtitle = nil;
self.latString = nil;
self.lngString = nil;
self.catMapId = nil;
self.catListingMapId = nil;
}
#end
MKMapViewDelegate having following delegate method,which is called every time when ever user tapped on calloutAccessory button, so here we get annotation title and subtitle,By using these values you can pass data to next viewController
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"%#",view.annotation.title);
NSLog(#"%#",view.annotation.subtitle);
}
What has the index of the selected row ([self.tableView indexPathForSelectedRow]) got to do with the annotation that someone has selected on the map? And where does catID come from?
If you want to show something on the detailViewController that is related to the annotation that someone has just pressed then you need to use info from the pin, currently you are using two data points that do not come from that pin, and are probably the same every time, so that's what you see on the detailViewController.
detailViewController.listingId = theAnnotation.catListingMapId
and in detailViewController find the other details by listingID
EDIT:
in
#interface MyAnnotation
write a method:
- (NSDictionary *) getTheAnnotationData{
NSDictionary* theDict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat:#"%f",latString],#"latString",
[NSString stringWithFormat:#"%f",lngString],#"lngString",
[NSString stringWithFormat:#"%i",catMapId],#"catMapId",
[NSString stringWithFormat:#"%i",catListingMapId],#"catListingMapId",
title,#"title",
subtitle,#"subtitle",
nil];
return theDict;
}
This method will give you all data from your annotation in a dictionary.
write it into .h too:
- (NSDictionary *) getTheAnnotationData;
then:
make a property in DetailViewController:
#property (nonatomic, retain) NSDictionary* detailDictionary;
then:
in your calloutAccessoryControlTapped method:
- (void)mapView:(MKMapView *)mv annotationView:(MKAnnotationView *)pin calloutAccessoryControlTapped:(UIControl *)control {
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
MyAnnotation *theAnnotation = (MyAnnotation *) pin.annotation;
NSLog(#"the Annotation %#",theAnnotation.catListingMapId);
detailViewController.detailDictionary = [theAnnotation getTheAnnotationData];
NSLog(#"detailViewController.listingId %#",detailViewController.listingId );
[self.navigationController pushViewController:detailViewController animated:YES];
}
then in detailViewController:
# synthesize detailDictionary
and in viewDidload:
you could get all of your data what you want about your annotation
I hope it helps
I was wondering how I would add pins to a MapView within an iPhone app. I want to have pins pinned in places that have the word "Tea" in their name and it would be impractical to place each pin in every place that contains that word, so I was wondering if there's some way to make it so that when the MapView is loaded, the pins are pinned into those places. I assume that this would be done with Google's Map API however I'm unsure as to how I'd exactly do this - does anyone know of any tutorials that would show to implement this.
So far, I have a simple view that contains a MapView as well as a corresponding view controller.
Thanks in advance!
You'll have to add instances of MKAnnotation to your MKMapView.
[mapView addAnnotation:annotation];
annotation is an instance of a class conforming to the MKAnnotation protocol. Read the corresponding documentation here:
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKAnnotation_Protocol/Reference/Reference.html#//apple_ref/occ/intf/MKAnnotation
Sample code:
#interface MyAnnotation: NSObject <MKAnnotation>
{
CLLocationCoordinate2D coordinate;
NSString *title;
}
#end
#implementation MyAnnotation
#synthesize coordinate, title;
- (id) init
{
if ((self = [super init]))
{
coordinate.latitude = 0.0;
coordinate.longitude = 0.0;
title = NSLocalizedString(#"Tea");
}
return self;
}
#end
In your view controller:
- (void) viewDidLoad
{
[super viewDidLoad];
// custom initialiation; create map view
[self addPin]; // or with parameters, called multiple times, to add several annotations
}
- (void) addPin
{
MyAnnotation *ann = [[MyAnnotation alloc] init];
[mapView addAnnotation:ann];
[ann release];
}
Hope this helps.
In my app I am using Apple's KMLViewer to show annotations that I get from a KML file.In the file KMLParser.m, there is an instance variable, placemarkDescription that converts the information under Description tags from kml file to annotation subtitle.Now, in my file every annotation has the information stored under Description in this way:
<table width="280px"><tr><td></td><td></td></tr></table><table width="280px"><tr><td><b>Fitness Bulls</b>---Palester sportive. Sporti dhe koha e lire.....<a href="http://www.site.com/BIZ_DIR/810180432/Article-Fitness-Bulls.aspx" style="color:Green;" >Shikoni detajet >></a></td></tr><tr><td><a href="http://www.site.com/HartaV2/AddReview.aspx?gisDataId=8123855e-b798-40bc-ad2e-00346a931211" style="color:Green;" >Shkruani pershtypjen tuaj >> </a> <p style="float:right;">Postuar nga:<i>Import</i></p></td></tr></table>
In KMLParser.m i have transformed the placemarkDescription from that to this:
<html><body>
<table width="280px"><tr><td></td><td></td></tr></table><table width="280px"><tr><td>
<b>Fitness Bulls</b>---Palester sportive. Sporti dhe koha e lire.....
<a href="http://www.site.com/BIZ_DIR/810180432/Article-Fitness-Bulls.aspx" style="color:Green;" >Shikoni detajet >></a></td></tr><tr><td>
<a href="http://www.site.com/HartaV2/AddReview.aspx?gisDataId=8123855e-b798-40bc-ad2e-00346a931211" style="color:Green;" >Shkruani pershtypjen tuaj >> </a> <p
style="float:right;">Postuar nga:<i>Import</i></p></td></tr></table>
</body></html>
I've done this because I want to pas this string to a webView and visualize this in it.
The problem is that when the kml loads, the methods get the description information, get called severe times.Exactly as times as placemarks stored in the kml.So passing the string directly has no effect.If i choose to set active the subtitle option (annotation.subtitle = placemarkDescription in KMLParser), maybe I gen get the subtitle info of the annotation the user tapped, but I don't want to show this information because it shows like this
<table width="280px"><tr><td></td><td></td></tr></table><table width="280px"><tr......
By the way, I don't have any idea of how to get the subtitle info of the selected annotation.
So far, I have managed only to store the description information in an array (done this in the KMLParser.m).But what should I do with that array?How to know to which array entry corresponds the annotation the user tapped (the annotation that has the callout bubble opened).
So I don't know what to do.
Maybe I have not been too clear: What I want to do is get the Description information of a placemark (annotation), when the user taps an annotation in the map, tapping the disclosureButton should redirect him to a webView that shows the description Information.
EDIT code Added:
DetailViewController.h
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController<UIWebViewDelegate> {
UIWebView *webView;
UITextField *addressBar;
UIActivityIndicatorView *activityIndicator;
NSString *placemarkDescription;
}
#property (nonatomic, retain) IBOutlet UIWebView *webView;
#property (nonatomic, retain) IBOutlet UITextField *addressBar;
#property (nonatomic, retain) IBOutlet UIActivityIndicatorView *activityIndicator;
#property (nonatomic, retain) NSString *placemarkDescription;
-(IBAction) gotoAddress:(id)sender;
-(IBAction) goBack:(id)sender;
-(IBAction) goForward:(id)sender;
#end
DetailViewController.m
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize webView, addressBar, activityIndicator, placemarkDescription;
- (void)viewDidLoad {
[super viewDidLoad];
[webView loadHTMLString:placemarkDescription baseURL:nil];
}
-(IBAction)gotoAddress:(id) sender {
NSURL *url = [NSURL URLWithString:[addressBar text]];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[webView loadRequest:requestObj];
[addressBar resignFirstResponder];
}
-(IBAction) goBack:(id)sender {
[webView goBack];
}
-(IBAction) goForward:(id)sender {
[webView goForward];
}
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
NSURL *URL = [request URL];
if ([[URL scheme] isEqualToString:#"http"]) {
[addressBar setText:[URL absoluteString]];
[self gotoAddress:nil];
}
return NO;
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[activityIndicator startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[activityIndicator stopAnimating];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[super dealloc];
}
#end
PlacemarkAnnotation2.h
#import <Foundation/Foundation.h>
#import <MapKit/Mapkit.h>
#interface PlacemarkAnnotation2 : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString * title;
NSString * subtitle;
NSString * placemarkDescription;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSString * subtitle;
#property (nonatomic, retain) NSString * placemarkDescription;
#end
PlacemarkAnnotation2.m
#import "PlacemarkAnnotation2.h"
#implementation PlacemarkAnnotation2
#synthesize coordinate, title, subtitle, placemarkDescription;
- (id) initWithCoordinate:(CLLocationCoordinate2D)coord andTitle:(NSString *)maintitle andSubtitle:(NSString *)subTitle {
self.coordinate = coord;
self.title = maintitle;
self.subtitle = subTitle;
return self;
}
-(NSString *) placemarkDescription
{
return placemarkDescription;
}
- (void) setPlacemarkDescription: (NSString *) pd
{
placemarkDescription = pd;
}
- (void) dealloc {
[title dealloc];
[subtitle dealloc];
[placemarkDescription dealloc];
[super dealloc];
}
#end
Changes in KMLParser.M
//KMLPoint class
- (MKShape *)mapkitShape
{
PlacemarkAnnotation2 *annotation = [[PlacemarkAnnotation2 alloc] init];
annotation.coordinate = point;
return [annotation autorelease];
}
//KMLPlacemark class
- (void)_createShape
{
if (!mkShape) {
mkShape = [[geometry mapkitShape] retain];
mkShape.title = name;
// Skip setting the subtitle for now because they're frequently
// too verbose for viewing on in a callout in most kml files.
NSString *lessThan = #"<";
NSString *greaterThan = #">";
placemarkDescription = [placemarkDescription stringByReplacingOccurrencesOfString:lessThan
withString:#"<"];
placemarkDescription = [placemarkDescription stringByReplacingOccurrencesOfString:greaterThan
withString:#">"];
NSString *beforeBody = #"<html><body>";
NSString *afterBody = #"</body></html>";
NSString *finalContent = [[beforeBody stringByAppendingString:placemarkDescription]
stringByAppendingString:afterBody];
placemarkDescription = finalContent;
mkShape.placemarkDescription = placemarkDescription;
}
}
Error found in this lines of code (No cause of crash description):
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#">>> Entering %s <<<", __PRETTY_FUNCTION__);
DetailViewController *dvc = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:[NSBundle mainBundle]];
PlacemarkAnnotation2 *pa = (PlacemarkAnnotation2 *)view.annotation;
dvc.placemarkDescription = pa.placemarkDescription;
[self presentModalViewController:dvc animated:YES];
[dvc release];
NSLog(#"<<< Leaving %s >>>", __PRETTY_FUNCTION__);
}
It's not clear how much of the KMLViewer sample app code you're using but one way to do this is to create your own annotation class instead of using the MKPointAnnotation class like the sample app does.
The custom class (eg. "PlacemarkAnnotation"), should implement the MKAnnotation protocol or be a sub-class of MKShape (if you are using the KMLViewer code). In the custom class, add a placemarkDescription property.
Where the KMLViewer code currently creates an MKPointAnnotation object, create a PlacemarkAnnotation instead and set its placemarkDescription property instead of the subtitle property.
Then in the viewForAnnotation delegate method, set the rightCalloutAccessoryView to a detail disclosure button.
Next, add to the project a detail view controller with a UIWebView in it. Add a placemarkDescription property to the view controller. In the viewDidLoad method, call loadHTMLString on the web view and pass it placemarkDescription (I think you can pass nil for the baseURL).
In the map view's calloutAccessoryControlTapped delegate method, create the detail view controller, set its placemarkDescription property and present it:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
DetailViewController *dvc = [[DetailViewController alloc] init...
PlacemarkAnnotation *pa = (PlacemarkAnnotation *)view.annotation;
dvc.placemarkDescription = pa.placemarkDescription;
[self presentModalViewController:dvc animated:YES];
[dvc release];
}
Edit:
First, it looks like it will be best to subclass MKShape for your custom class instead of implementing the MKAnnotation protocol. The rest of the KMLViewer code is based on this assumption. So change #interface PlacemarkAnnotation2 : NSObject <MKAnnotation> to #interface PlacemarkAnnotation2 : MKShape. (By the way, for the NSString properties, copy is more appropriate than retain and it'll get rid of warnings.)
It also looks like you may have changed the type of the mkShape ivar in KMLPlacemark (and other places) from MKShape to something else. Change these types back to MKShape.
Next, _createShape might not be the best place to set the placemarkDescription since that method is called for both overlays and annotations. Remove your changes from that method and put them in the point method (also in KMLPlacemark). Note there are a couple of potential memory-related issues with your changes. Here's my suggestion:
- (void)_createShape
{
if (!mkShape) {
mkShape = [[geometry mapkitShape] retain];
mkShape.title = name;
// Skip setting the subtitle for now because they're frequently
// too verbose for viewing on in a callout in most kml files.
}
}
- (id <MKAnnotation>)point
{
[self _createShape];
if ([mkShape isKindOfClass:[PlacemarkAnnotation2 class]])
{
if (placemarkDescription != nil)
//check for nil, otherwise will crash when
//passing to stringByAppendingString below
{
NSString *lessThan = #"<";
NSString *greaterThan = #">";
placemarkDescription = [placemarkDescription stringByReplacingOccurrencesOfString:lessThan
withString:#"<"];
placemarkDescription = [placemarkDescription stringByReplacingOccurrencesOfString:greaterThan
withString:#">"];
NSString *beforeBody = #"<html><body>";
NSString *afterBody = #"</body></html>";
NSString *finalContent = [[beforeBody stringByAppendingString:placemarkDescription]
stringByAppendingString:afterBody];
placemarkDescription = [finalContent retain];
//added retain above since finalContent is autoreleased
//and we are setting the ivar manually. otherwise,
//can result in EXC_BAD_ACCESS later.
}
PlacemarkAnnotation2 *pa2 = (PlacemarkAnnotation2 *)mkShape;
pa2.placemarkDescription = placemarkDescription;
return (id <MKAnnotation>)mkShape;
}
return nil;
}
In my MapView, i read data from SQLite and display pins on it (up to 5000 record).
the database has the structure of ID| Longitude| Latitude | Title | subtitle
i used this code to make the pin clickable:
pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
i need to add a new column (Clickable) in the database, and make the pin clickable just if the "Clickable" value is ON.
any detailed suggestion about the best idea to do that?
From my experience if you don't set any property of the annotation (title,subTitle,image,accessory-button) and tap on the pin, the callout is not displayed.
Instead, if you want show the callout but not call an action when the accessory button is tapped, you could use a thing like this:
(After downloading the data from the db, you could store each item as a NSDictionary and then all items in a NSArray)
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
NSString *clickable=[[yourArray objectAtIndex:yourIndex] objectForKey:#"clickable"];
if(![clickable isEqualToString:#"YES"]){
return;
}
}
Of course I used a string as example, you may also use NSNumbers or BOOLs.
Hope i understood your question.
To control whether the callout has the accessory button based on the "clickable" flag in the table, I suggest you add a clickable property to your annotation class and set it when adding the annotation. Then you can check this property when creating the annotation view in the viewForAnnotation method.
To add the annotations to the map view (ie. when you call addAnnotation), if you are currently using a pre-defined class like MKPointAnnotation, you'll need to instead define your own custom class that implements the MKAnnotation protocol. If you already have a custom class, add a clickable property to it.
Here's an example of a custom annotation class:
//CustomAnnotation.h...
#interface CustomAnnotation : NSObject<MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property (nonatomic, assign) int annotationId;
#property (nonatomic, assign) BOOL clickable;
#end
//CustomAnnotation.m...
#implementation CustomAnnotation
#synthesize coordinate;
#synthesize title;
#synthesize subtitle;
#synthesize annotationId;
#synthesize clickable;
- (void)dealloc {
[title release];
[subtitle release];
[super dealloc];
}
#end
Here's an example of how you would create and add the annotations (the actual values for the annotation properties will come from your sql rows):
CustomAnnotation *ca1 = [[CustomAnnotation alloc] init];
ca1.annotationId = 1;
ca1.coordinate = CLLocationCoordinate2DMake(lat1,long1);
ca1.title = #"clickable";
ca1.subtitle = #"clickable subtitle";
ca1.clickable = YES;
[myMapView addAnnotation:ca1];
[ca1 release];
CustomAnnotation *ca2 = [[CustomAnnotation alloc] init];
ca2.annotationId = 2;
ca2.coordinate = CLLocationCoordinate2DMake(lat2,long2);
ca2.title = #"not clickable";
ca2.subtitle = #"not clickable subtitle";
ca2.clickable = NO;
[myMapView addAnnotation:ca2];
[ca2 release];
Then the viewForAnnotation will look like this:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString *reuseId = #"CustomAnnotation";
MKPinAnnotationView* customPinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (customPinView == nil) {
customPinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId] autorelease];
customPinView.pinColor = MKPinAnnotationColorPurple;
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
}
else {
customPinView.annotation = annotation;
}
CustomAnnotation *ca = (CustomAnnotation *)annotation;
if (ca.clickable)
customPinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
else
customPinView.rightCalloutAccessoryView = nil;
return customPinView;
}
Finally, you can handle the button press in the calloutAccessoryControlTapped delegate method and access your custom annotation's properties:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
CustomAnnotation *ca = (CustomAnnotation *)view.annotation;
NSLog(#"annotation tapped, id=%d, title=%#", ca.annotationId, ca.title);
}
A custom AnnotationView is updated with new coordinates. But the problem is that it visually updates only after some manipulations with MKMapView, e.g. zooming or moving.
What should I do to manually update visual position on a map?
PS. I've tried to change region to current map's region. But it does change zoom. It's strange.
[mapView setRegion:[mapView region] animated:YES];
I am a little shoked after hours of research. The answer is just:
[mapView setCenterCoordinate:mapView.region.center animated:NO];
Do not ask me why, but it updates a mapview and it's what i was need.
MKMapView observes the coordinate property of annotations via KVO. You simply need to observe proper KVO protocol and send the annotation willChangeValueForKey: and didChangeValueForKey: with keypath of #"coordinate" before and after you update the coordinates.
Likewise title and subtitle are also observed by MKMapView. so if you update those and want the value in the callout to change automatically without any effort on your part, just do the same: call willChangeValueForKey: and didChangeValueForKey:
if your adding your annoations from a thread it wont work.
i had the same problem and just wrapping my function that was adding the annotations with the following worked
[self performSelectorOnMainThread:#selector(addCameraIconOnMain:) obj waitUntilDone:true];
-(void) addCameraIconOnMain:(myobjecttype*)obj
{
// this isnt the entire function, customize for your own purpose.....
[mapView addAnnotation:annotation];
}
The answer here is NOT to refresh the MapView or the Annotation!
the coordinate property of MKAnnotation has KVO on it. If you just add the id pointer, of the object you want on the map, to the mapview and update the coordinate property with a new location, MKMapView will do the rest for you.
As close as you can get to a free lunch!
I solved this error with an asynchronous call, at least 0.5 delay.
e.g.: [self performSelector:#selector(redrawPins) withObject:nil afterDelay:0.5];
Where "redrawPins" is the function which adds and removes pins.
There's no reason you can't remove and then re-add the annotation. That's probably way more performant than moving the entire map, even if its a fake move.
Here is the interface to MapAnnotation:
// CSMapAnnotation.h
// mapLines
// Created by Craig on 5/15/09.
// Copyright 2009 Craig Spitzkoff. All rights reserved.
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
// types of annotations for which we will provide annotation views.
typedef enum {
MapAnnotationTypeStart = 0,
MapAnnotationTypeEnd = 1,
MapAnnotationTypeImage = 2
} MapAnnotationType;
#interface MapAnnotation : NSObject <MKAnnotation>
{
CLLocationCoordinate2D _coordinate;
MapAnnotationType _annotationType;
NSString* _title;
NSString* _subtitle;
NSString* _userData;
NSString* speed;
NSString* identifier;
}
#property (nonatomic, retain) NSString *speed;
#property (nonatomic, retain) NSString *identifier;
-(id) initWithCoordinate:(CLLocationCoordinate2D)coordinate
annotationType: (MapAnnotationType) annotationType
title: (NSString*) title
subtitle: (NSString*) subtitle
speed: (NSString *) speed
identifier: (NSString *) identifier;
-(id) setWithCoordinate: (CLLocationCoordinate2D) coordinate
annotationType: (MapAnnotationType) annotationType
title: (NSString*) title
subtitle: (NSString*) subtitle
speed: (NSString*) speed
identifier: (NSString*) identifier;
#property MapAnnotationType annotationType;
#property (nonatomic, retain) NSString* userData;
#end
And here is the implementation:
// CSMapAnnotation.m
// mapLines
// Created by Craig on 5/15/09.
// Copyright 2009 Craig Spitzkoff. All rights reserved.
#import "MapAnnotation.h"
#implementation MapAnnotation
#synthesize coordinate = _coordinate;
#synthesize annotationType = _annotationType;
#synthesize userData = _userData;
#synthesize speed;
#synthesize identifier;
-(id) initWithCoordinate:(CLLocationCoordinate2D)coordinate
annotationType: (MapAnnotationType) annotationType
title: (NSString*)title
subtitle: (NSString*) subtitle
speed: (NSString *) speedz
identifier: (NSString *) identifierz
{
self = [super init];
_coordinate = coordinate;
_title = [title retain];
_subtitle = [subtitle retain];
_annotationType = annotationType;
speed = speedz;
identifier = identifierz;
return self;
}
-(id) setWithCoordinate:(CLLocationCoordinate2D)coordinate
annotationType: (MapAnnotationType) annotationType
title: (NSString*) title
subtitle: (NSString*) subtitle
speed: (NSString*) speedz
identifier: (NSString*) identifierz
{
_coordinate = coordinate;
_title = [title retain];
_subtitle = [subtitle retain];
_annotationType = annotationType;
speed = speedz;
identifier = identifierz;
return self;
}
-(NSString*) title
{
return _title;
}
-(NSString*) subtitle
{
return _subtitle;
}
-(void) dealloc
{
[_title release];
[_userData release];
[super dealloc];
}
#end