MKPinannotation detail disclosure button - present new view

Jordan Brown picture Jordan Brown · Jun 1, 2011 · Viewed 10k times · Source

FirstViewController.h

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@interface TransparentToolbar : UIToolbar{

}
@end
@interface AddressAnnotation : NSObject <MKAnnotation>  {

CLLocationCoordinate2D coordinate;

NSString *title;
  NSString *subTitle;
 NSString *directions;
  NSString *website; 

}
@property (nonatomic,retain) NSString *title;
@property (nonatomic,retain) NSString *subtitle;
@property (nonatomic,retain) NSString *directions;
@property (nonatomic,retain) NSString *website; 


@end


@interface FirstViewController : UIViewController {
IBOutlet MKMapView  *mapView;
//stores go here!
//declare store names as Company *cityState
AddressAnnotation *chiliAuburnAlabama;
AddressAnnotation *tuttifruttiHomewoodAlabama;
NSString *website;
}
@property (nonatomic,retain) NSString *website;
-(IBAction) updateLocation;
-(IBAction) setMap:(id)sender;
-(IBAction) showPin;
@end


FirstViewController.m

#import "FirstViewController.h"
#import "MoreInfo.h"
@implementation AddressAnnotation
@synthesize title;
@synthesize subtitle;
@synthesize coordinate;
@synthesize directions;
@synthesize website;
- (NSString *)subtitle{
return subtitle;

}
-(NSString *) website{
return website;
}  

-(NSString *) directions{
return directions;
}

- (NSString *)title{
return title;
}

-(id)initWithCoordinate:(CLLocationCoordinate2D) c{
coordinate=c;
return self;
}

@end





@implementation TransparentToolbar

// Override draw rect to avoid
// background coloring
- (void)drawRect:(CGRect)rect {
// do nothing in here
}

// Set properties to make background
// translucent.
- (void) applyTranslucentBackground
{
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
self.translucent = YES;
}

// Override init.
 - (id) init
{
self = [super init];
[self applyTranslucentBackground];
return self;
 }

// Override initWithFrame.
- (id) initWithFrame:(CGRect) frame
 {
self = [super initWithFrame:frame];
[self applyTranslucentBackground];
return self;
}

@end

 @implementation FirstViewController
 @synthesize website;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)mapViewWillStartLocatingUser:(MKMapView *)mapView{


}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
[self showPin];
}
- (void)viewDidLoad
{  
mapView.showsUserLocation = YES;
//[self showPin];
[super viewDidLoad];




}

-(IBAction) updateLocation{
mapView.showsUserLocation = YES;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
 {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}


- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc. that aren't in use.
}


- (void)viewDidUnload
{
[super viewDidUnload];

// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}


-(IBAction)setMap:(id)sender{
switch(((UISegmentedControl *)sender).selectedSegmentIndex){
case 0:
{
    mapView.mapType = MKMapTypeStandard;
     break;
}
case 1:
{
    mapView.mapType = MKMapTypeSatellite;
    break;
}
case 2:
{
mapView.mapType = MKMapTypeHybrid;
break;
}

}}


- (MKAnnotationView *)mapView:(MKMapView *)mv viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"pinView"];

if (!pinView) {
    pinView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"pinView"] autorelease];
   pinView.image = [UIImage imageNamed:@"SPOON4.png"];
    pinView.frame = CGRectMake(-30, 0, 37.5, 67.5); 
    //pinView.animatesDrop = YES;
    pinView.canShowCallout = YES;


    UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    pinView.rightCalloutAccessoryView = rightButton;

} else {
    pinView.annotation = annotation;
}
if (annotation == mapView.userLocation){
    return nil; //default to blue dot
}
return pinView;
}
-(IBAction) showPin{

MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta=0.2;
span.longitudeDelta=0.2;
//MOST CODE WILL BE INSERTED HERE!
//INSTRUCTIONS!
//CCLocationCoordinate2D companyCity = mapView.userLocation.coordinate;
//companyCity.latitude = latitude here;
//companyCity.longitutde = longitude here;
//CLLocation *companyCityLocation = [[CLLocation alloc] initWithLatitude:companyCity.latitude longitude:companyCity.longitude];
//if(companyCityState != nil) {
//[mapView removeAnnotation:companyCityState];
//[companyCityState release];
//companyCityState = nil;
//}
//companyCityState = [[AddressAnnotation alloc] initWithCoordinate:companyCity];
//companyCityState.title = @"name of shop here";
//double distanceMetersCompanyCityState = [mapView.userLocation.location distanceFromLocation:companyCityLocation];
//double distanceMilesCompanyCityState = distanceMetersCompanyCityState/1609.334;
//companyCityState.subtitle= [NSString stringWithFormat:@"%.2f miles away", distanceMilesCompanyCityState];
//[mapView addAnnotation:companyCityState];
//[companyCityState release];

//always declare location as companyCity 
//if more than one in one city then add a number i.e. companyCity2

//chili in mobile alabama
CLLocationCoordinate2D chiliAuburn = mapView.userLocation.coordinate;
chiliAuburn.latitude = 32.606434 ;
chiliAuburn.longitude = (-85.484025);
CLLocation *chiliAuburnLocation = [[CLLocation alloc] initWithLatitude:chiliAuburn.latitude longitude:chiliAuburn.longitude];




if(chiliAuburnAlabama != nil) {
    [mapView removeAnnotation:chiliAuburnAlabama];
    [chiliAuburnAlabama release];
    chiliAuburnAlabama = nil;
}

chiliAuburnAlabama = [[AddressAnnotation alloc] initWithCoordinate:chiliAuburn];
chiliAuburnAlabama.title = @"Chili Yogurt Café";
chiliAuburnAlabama.website = @"http://stackoverflow.com/questions/6195774/mkpinannotation-detail-disclosure-button-get-directions-call-number";
double distanceMetersChiliAuburnAlabama = [mapView.userLocation.location distanceFromLocation:chiliAuburnLocation];
double distanceMilesChiliAuburnAlabama = distanceMetersChiliAuburnAlabama/1609.334;
chiliAuburnAlabama.subtitle= [NSString stringWithFormat:@"%.1f miles away", distanceMilesChiliAuburnAlabama];
[mapView addAnnotation:chiliAuburnAlabama];
[chiliAuburnAlabama release];
//tutti frutti homewood alabama

CLLocationCoordinate2D tuttifruttiHomewood = mapView.userLocation.coordinate;
tuttifruttiHomewood.latitude = 33.479775 ;
tuttifruttiHomewood.longitude = -86.790977;
CLLocation *tuttifruttiHomewoodLocation = [[CLLocation alloc]initWithLatitude:tuttifruttiHomewood.latitude longitude:tuttifruttiHomewood.longitude];
if(tuttifruttiHomewoodAlabama != nil) {
    [mapView removeAnnotation:tuttifruttiHomewoodAlabama];
    [tuttifruttiHomewoodAlabama release];
    tuttifruttiHomewoodAlabama = nil;
}

tuttifruttiHomewoodAlabama = [[AddressAnnotation alloc] initWithCoordinate:tuttifruttiHomewood];
double distanceMetersTuttifruttiHomewoodAlabama = [mapView.userLocation.location distanceFromLocation:tuttifruttiHomewoodLocation];
double distanceMilesTuttifruttiHomewoodAlabama = distanceMetersTuttifruttiHomewoodAlabama/1609.334;
tuttifruttiHomewoodAlabama.title = @"Tutti Frutti";
tuttifruttiHomewoodAlabama.subtitle = [NSString stringWithFormat:@"%.1f miles away",distanceMilesTuttifruttiHomewoodAlabama];
[mapView addAnnotation:tuttifruttiHomewoodAlabama];




//ignore below
CLLocationCoordinate2D user = mapView.userLocation.coordinate;
region.span = span;
region.center = user;
[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];

}
- (void)mapView:(MKMapView *)mv didAddAnnotationViews:(NSArray *)views {
for(MKAnnotationView *annotationView in views) {
    if(annotationView.annotation == mv.userLocation) {
        MKCoordinateRegion region;
        MKCoordinateSpan span;

        span.latitudeDelta=0.1;
        span.longitudeDelta=0.1; 

        CLLocationCoordinate2D location=mv.userLocation.coordinate;

        region.span=span;
        region.center=location;

        [mv setRegion:region animated:TRUE];
        [mv regionThatFits:region];
    }

}
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
MoreInfo *moreInfoView = [[MoreInfo alloc] initWithNibName:@"MoreInfo" bundle:nil];
moreInfoView.title = view.annotation.title ;
//moreInfoView.getDirections = @"";
moreInfoView.getWebsite = [NSURL URLWithString:view.annotation.website];
[self.navigationController pushViewController:moreInfoView animated:YES];




}

- (void)dealloc
{   

[super dealloc];    
}
@end

MoreInfo.h

#import <UIKit/UIKit.h>


@interface MoreInfo : UIViewController {
IBOutlet UITableView *tableView;
NSMutableArray *moreInfo;
 NSURL *getDirections;
 NSURL *getWebsite;
}
-(void)goToWebsite;
@property (nonatomic,retain) NSURL *getDirections;
@property (nonatomic,retain) NSURL *getWebsite;

@end

MoreInfo.m

#import "MoreInfo.h"
#import "FirstViewController.h"

@implementation MoreInfo
@synthesize getDirections;
@synthesize getWebsite;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
    // Custom initialization
}
return self;
}

- (void)dealloc
{
[super dealloc];
}

- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad {
moreInfo = [[NSMutableArray alloc] init];
[moreInfo addObject:@"Phone Number"];
[moreInfo addObject:@"Address"];
[moreInfo addObject:@"Go to Website"];
[moreInfo addObject:@"Get Directions"];
    [super viewDidLoad];
}
/*- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView    accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath{

}*/
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{


if (indexPath.row == 2) {
    [self goToWebsite];
}



}
-(void) goToWebsite{
[[UIApplication sharedApplication]openURL:getWebsite];
NSLog(@"website is %@", [getWebsite absoluteString]);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return [moreInfo count];

}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
if (section == 0) {
    return @"More Information";
}else return nil;

} 
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}


// Set up the cell...
if(indexPath.section == 0){
    cell.textLabel.text = [moreInfo objectAtIndex:indexPath.row];
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
} return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{
if(section == 0){
    return @"Touch the Address to Get Directions";
}else return nil;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

@end

Answer

justin picture justin · Jun 1, 2011

If I understand correctly, you want to add a disclosure button that would allow you to present a new view with information about the current pin annotation. To get a disclosure button, you just need to implement this code:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
    MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"pinView"];
    if (!pinView) {
        pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"pinView"] autorelease];
        pinView.pinColor = MKPinAnnotationColorRed;
        pinView.animatesDrop = YES;
        pinView.canShowCallout = YES;

        UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        pinView.rightCalloutAccessoryView = rightButton;
    } else {
        pinView.annotation = annotation;
    }
    return pinView;
}

Now when you tap on a pin on the mapView, a disclosure button will display in the presented view. You will then need to use the following method to tell the app what to do when the disclosure button is pressed.

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control

In that method, you can present a popover or modal window or push a view or whatever you would like to do with information about the current location. You don't need to create a new nib for each one, either. The easiest thing to do would be to synthesize values in that view controller such as phoneNumber and website and things like that. Then in the last method there where you present the view, pass the values in as needed. For example,

NewView *vc = [[NewView alloc] initWithNibName:@"NewView" bundle:nil];
vc.phoneNumber = // the phone number of this location;
vc.website = // the website;

and so on before you present the view. I hope this helps

EDIT: To get rid of that error, for everything you synthesize, you must declare in the header. So you'll have

@interface FirstViewController : UIViewController {
    IBOutlet MKMapView  *mapView;
    //stores go here!
    //declare store names as Company *cityState
    AddressAnnotation *chiliAuburnAlabama;
    AddressAnnotation *tuttifruttiHomewoodAlabama;

    NSString *website;
    // the rest of your synthesized attributes you want in the view controller;
}
-(IBAction) updateLocation;
-(IBAction) setMap:(id)sender;
-(IBAction) showPin;

@property (nonatomic, retain) NSString *website;
// This is the property that will get rid of your error and you should do it to any other attributes you want to pass into the controller;
@end

You will want that in the annotation header as well if you use it there. It tells that part of the code that it should expect some string to be sent to it and it will know how to handle storing it.

As for calling your GoToMap method, you should set up a delegate for your MoreInfo class when you create it. So in your MoreInfo header, you'll have, among everything else

@interface MoreInfo .... {
    id delegate;
    // everything else;
}
@property (nonatomic, assign) id delegate;
// your methods and other properties;
@end

Then when you create it in your view controller, you'll have

MoreInfo *moreInfoView = [[MoreInfo alloc] initWithNibName:@"MoreInfo" bundle:nil];
moreInfoView.title = view.annotation.title ;
moreInfoView.delegate = self;
// this assigns the current view controller as its delegate;
//moreInfoView.getDirections = [NSURL URLWithString:[NSString stringWithFormat: @"http://maps.google.com/maps?q=%@@%1.6f,%1.6f&z=10", view.annotation.coordinate.latitude, view.annotation.coordinate.longitude]];
moreInfoView.getWebsite = view.annotation.website;
[self.navigationController pushViewController:moreInfoView animated:YES];

Finally, when you want to call GoToWebsite from your MoreInfo, you can call

[self.delegate GoToWebsite];

This of course is assuming that method is in your first view controller (which I could swear it was, but can't find it all the sudden).

But that's basically how you would go about it