I'm working on a map based application with annotations. I am hoping to connect the accessory button in the annotation callout to a segue to a detail view controller.
I have added a new view controller in storyboard with a push segue from the view controller of the map view with the identify firePit
My ViewController.m follows as
#import "ViewController.h"
#import "Annotations.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
[locationManager startUpdatingLocation];
_mapView.showsUserLocation = YES;
_mapView.delegate = self;
//Setting the Visable Region
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(45.036179, -87.134691);
MKCoordinateRegion adjustedRegion = [_mapView regionThatFits:MKCoordinateRegionMakeWithDistance(center, 2000, 1900)];
[self.mapView setRegion: adjustedRegion animated:YES];
//Annotations - Is this the best place?
CLLocationCoordinate2D firePitCoord = CLLocationCoordinate2DMake( 45.037559, -87.130526);
Annotations *firePit = [[Annotations alloc] initWithTitle:#"Fire Pit" Location:firePitCoord];
[self.mapView addAnnotation:firePit];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"Fire Pit";
if ([annotation isKindOfClass:[Annotations class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
} else {
annotationView.annotation = annotation;
}
return annotationView;
}
return nil;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:#"firePit" sender:view]; //thread breakdown 3.1
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(MKAnnotationView *)sender
{
if ([segue.identifier isEqualToString:#"firePit"])
{
MKAnnotationView *annotationView = sender;
[segue.destinationViewController setAnnotation:annotationView.annotation];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The app functions in the simulator until I press the accessory button in the annotation callout. It then crashes and alerts me to a thread breakdown in the [self performSegueWithIdentifier:#"firePit" sender:view] method (as commented in the code).
If anyone can offer insight into this problem I would really appreciate it. I can't seem to find assistance from any other resources but if you you have a recommendation I would love a link.
Thanks
Related
So, I've created a custom callout that appears over the selected annotation. Everything works exactly how I want--the only problem is after I've dropped another pin of the same type, then all the past pins will display the same information. For example, I may have a pin drop that displays in the callout "McDonalds" and the next pin that drops displays "Starbucks". Now all my previously dropped pins will display "Starbucks".
I'm currently attempting to store information in the annotation's subtitle property and then displaying that through a formatted string onto my custom UIView's Label...It works but I need the annotation's information to never change. There must be something I'm missing or do not understand. Any help will be much appreciated.
I've posted all the code I believe to be relevant below. Thank you!
Custom Callout.h
#import <UIKit/UIKit.h>
#interface PinView : UIView
{
UIView *view;
UILabel *theValueLabel;
}
#property (nonatomic, retain) IBOutlet UIView *view;
#property (nonatomic, retain) IBOutlet UILabel *theValueLabel;
#end
Custom Callout.m
#import "PinView.h"
#implementation PinView
#synthesize theValueLabel;
#synthesize view;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIView *nib = [[[UINib nibWithNibName:#"customView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
[self addSubview:nib];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
if (self.subviews.count == 0) {
UIView *nib = [[[UINib nibWithNibName:#"customView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
[self addSubview:nib];
}
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self setup];
}
- (void)setup {
self.theValueLabel.text = #"foo";
}
in my main View Controller.m
#interface BreadTrailViewController ()<CLLocationManagerDelegate, MKMapViewDelegate, MFMailComposeViewControllerDelegate>
{
PinView *aView
MKPointAnnotation *Pin1;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
if (location != nil)
{
Pin1 = [[MKPointAnnotation alloc] init];
Pin1.title = #"Venue";
Pin1.subtitle = [NSString stringWithFormat:#"Venue Name: %#\n%f,%f\nAddress: %#",revGeocodeVenue, lat, lng, revGeocodeAddress];
Pin1.coordinate = location.coordinate;
[self.mapView addAnnotation:Pin1];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// Handle any custom annotations.
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
pinView.canShowCallout = NO;
} else {
pinView.annotation = annotation;
}
if ([[annotation title] containsString:#"Venue"])
{
pinView.pinTintColor = [UIColor greenColor];
}
return pinView;
}
return nil;
}
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
aView = [[PinView alloc] initWithFrame:CGRectMake( 0, 0, 300, 350)];
aView.layer.cornerRadius = 20;
aView.layer.masksToBounds = YES;
aView.center = CGPointMake(view.bounds.size.width*0.5f, -aView.bounds.size.height*0.35f);
//Using the Pin's subtitle to store the data string and display on PinView
if ([[view.annotation title] containsString:#"Venue"])
{
aView.theValueLabel.text = Pin1.subtitle;
}
[view addSubview:aView];
}
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
[aView removeFromSuperview];
}
I found the problem! I was using Pin1.subtitle when what I should be using is view.annotation.subtitle--everything is working perfectly now. I hope this helps someone else!
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
aView = [[PinView alloc] initWithFrame:CGRectMake( 0, 0, 300, 350)];
aView.layer.cornerRadius = 20;
aView.layer.masksToBounds = YES;
aView.center = CGPointMake(view.bounds.size.width*0.5f, -aView.bounds.size.height*0.35f);
//Using the Pin's subtitle to store the data string and display on PinView
if ([[view.annotation title] containsString:#"Venue"])
{
aView.theValueLabel.text = view.annotation.subtitle;
}
[view addSubview:aView];
}
I am trying to implement drag/drop annotation on map. Initially the pin is dropped at user's current location which i do with [self.mapView setShowsUserLocation:YES]; but when i drag and drop the pin it returns back to the current location after some time.
Here is the code:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize mapView;
- (void)viewDidLoad {
[super viewDidLoad];
mapView.delegate = self;
}
- (MKAnnotationView *)mapView:(MKMapView *)mv viewForAnnotation:(id <MKAnnotation>)annotation
{
//MKAnnotationView* annotationView = nil;
MKAnnotationView *annotationView = [mv dequeueReusableAnnotationViewWithIdentifier:#"PinAnnotationView"];
if (!annotationView) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"PinAnnotationView"];
annotationView.draggable = YES;
}
[self.locationManager stopUpdatingLocation];
return annotationView;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState
{
if(newState == MKAnnotationViewDragStateStarting) {
CLLocationCoordinate2D droppedFrom = annotationView.annotation.coordinate;
NSLog(#"dropped from %f, %f", droppedFrom.latitude , droppedFrom.longitude);
// annotationView.dragState = MKAnnotationViewDragStateDragging;
}
else if (newState == MKAnnotationViewDragStateEnding)
{
CLLocationCoordinate2D droppedAt = annotationView.annotation.coordinate;
NSLog(#"dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
annotationView.dragState = MKAnnotationViewDragStateNone;
}
else if (newState == MKAnnotationViewDragStateCanceling) {
// custom code when drag canceled...
// tell the annotation view that the drag is done
[annotationView setDragState:MKAnnotationViewDragStateNone animated:YES];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[self.mapView setShowsUserLocation:YES];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
What am i missing?
Thanks in advance.
Hi Please check the answer of Jordan in this give the best solution for pin change Click Here
In my app I am displaying a tableview which contains result of searched user from database. Now in that tableview there is one button by clicking on that user can see location of all searched users in map.when user first click on button he will be able to see all user's location with green color pin. I am displaying map in half part of view. Now When user select any particular searched user from table I want to change color of that user's pin.And if user select any other user from table previous selected user's pin color should be changed to as it was before.
I have tried this:
- (void)selectAnnotation:(id < MKAnnotation >)annotation animated:(BOOL)animated{
MKPinAnnotationView *chng=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
chng.pinColor=MKPinAnnotationColorRed;
NSLog(#"========>selected");
}
And I am calling this method when user select particular user from table.
CLLocationCoordinate2D CurrentCoordinateSingleUser;
CurrentCoordinateSingleUser.latitude=[[singleUserPin objectAtIndex:1] doubleValue];
CurrentCoordinateSingleUser.longitude=[[singleUserPin objectAtIndex:2] doubleValue];
MapObjects *map1=[[MapObjects alloc]initwithCoordinate:CurrentCoordinateSingleUser title:[singleUserPin objectAtIndex:0] subtitle:Nil];
[userMap addAnnotation:map1];
for (id<MKAnnotation> currentAnnotation in userMap.annotations) {
if ([currentAnnotation isEqual:annotationToSelect]) {
[userMap selectAnnotation:currentAnnotation animated:FALSE];
}
}
But here selectAnnotation is not working.
UPDATE::
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *identifier = #"myAnnotation";
MKPinAnnotationView * annotationView = (MKPinAnnotationView*)[userMap dequeueReusableAnnotationViewWithIdentifier:identifier];
if([annotation isEqual:map1]){
annotationView.pinColor = MKPinAnnotationColorRed;
}
else {
if (!annotationView)
{
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
// if(fromSelectedTab==TRUE){
// annotationView.pinColor = MKPinAnnotationColorRed;
// annotationView.animatesDrop = NO;
// fromSelectedTab=FALSE;
// }
// else{
annotationView.pinColor = MKPinAnnotationColorGreen;
annotationView.animatesDrop = NO;
// }
annotationView.canShowCallout = YES;
// annotationView.dr
}
else {
annotationView.annotation = annotation;
}
}
//annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return annotationView;
}
MapObjects.h:
#interface MapObjects : NSObject<MKMapViewDelegate,MKAnnotation>
{
NSString *title,*subtitle;
CLLocationCoordinate2D coordinate;
}
#property(nonatomic,copy)NSString *title;
#property(nonatomic,copy)NSString *subtitle;
#property(nonatomic) CLLocationCoordinate2D coordinate;
-(MapObjects *)initwithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle;
#end
Use :
- (MKAnnotationView *)viewForAnnotation:(id <MKAnnotation>)annotation;
instead of creating a new MKPinAnnotationView. Or if it's in purpose you have to add somewhere the new annotation in your map.
Hope that will help
You should override MKMapiew method
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation (id<MKAnnotation>)annotation {
static NSString *identifier = #"PinAnnotation";
// map1 should be visible in method context
if ([annotation isEqual:map1]) {
MKPinAnnotationView *pinAnnotation = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
pinAnnotation.pinColor = MKPinAnnotationColorRed;// or any you want
return pinAnnotation;
}
return nil;
}
There you can determine specified annotation and set it desired color.
in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// There you should have selected "user" object and "users" array
...
// _annotations is declared in header
_annotations = [[NSMutableArray alloc] initWithCapacity:0];
for (UserObj *obj in users) {
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(0.f, 0.f);
coordinate.latitude = obj.latitude;
coordinate.longitude = obj.longitude;
MapObjects *userAnnotation = [[MapObjects alloc] initWithCoordinate:coordinate title:obj.name subtitle:nil];
[map addAnnotation:userAnnotation];
[_annotations addObject:userAnnotation];
}
...
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKPinAnnotationView *)view {
view.pinColor = MKPinAnnotationColorRed;
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKPinAnnotationView *)view {
view.pinColor = MKPinAnnotationColorGreen;
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation (id<MKAnnotation>)annotation {
static NSString *identifier = #"PinAnnotation";
MKPinAnnotationView *pinAnnotation = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if(!pinAnnotation) {
pinAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
}
pinAnnotation.pinColor = MKPinAnnotationColorGreen;
return pinAnnotation;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// You should keep your annotation objects in some array too because map.annotations won't return annotation in order that we need
// After you add annotation to map, add them to NSMutableArray too, [_annotations addObject:userAnnotation];
MapObjects annotation = _annotations[indexPath.row];// not sure that it return that exact annotation
[map selectAnnotation:annotation animated:YES];
}
Why can not add annotations on mapView??? I get array with annotation, then try to put them to map((( map is empty( this my code and viewAnnotation method where i find mistake with my own annotation class(
#implimentation MyClass {
NSMutableArray *_annotation;
}
_agents = [[Database sharedDatabase] agentsList];
_mapView.delegate = self;
[_agents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_annotations addObject:[[[BronAgentMapViewAnnotation alloc] initWithAgency:obj] autorelease]];
}];
NSLog(#"num of annot = %d", _annotations.count); //num of annot = 120
_mapView.showsUserLocation = YES;
if (_showUserLocation) {
_mapView.showsUserLocation = YES;
}
[_annotations enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_mapView addAnnotation: obj];
}];
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
_annotations = [[NSMutableArray array] retain];
}
return self;
}
- (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;
// handle our three custom annotations
//
if ([annotation isKindOfClass:[BronAgentMapViewAnnotation class]])
{
// try to dequeue an existing pin view first
static NSString *bronAgentMapViewAnnotationIdentifier = #"BronAgentMapViewAnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)
[_mapView dequeueReusableAnnotationViewWithIdentifier:bronAgentMapViewAnnotationIdentifier];
if (pinView == nil)
{
// if an existing pin view was not available, create one
MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:bronAgentMapViewAnnotationIdentifier] autorelease];
customPinView.pinColor = MKPinAnnotationColorPurple;
customPinView.animatesDrop = NO;
customPinView.canShowCallout = YES;
// add a detail disclosure button to the callout which will open a new view controller page
//
// note: you can assign a specific call out accessory view, or as MKMapViewDelegate you can implement:
// - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control;
//
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self
action:#selector(showDetails:)
forControlEvents:UIControlEventTouchUpInside];
rightButton.tag = [(BronAgentMapViewAnnotation *) annotation annotationId];
customPinView.hidden = [(BronAgentMapViewAnnotation *) annotation isHidden];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
}
else
{
pinView.rightCalloutAccessoryView.tag = [(BronAgentMapViewAnnotation *) annotation annotationId];
pinView.hidden = [(BronAgentMapViewAnnotation *) annotation isHidden];
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
Can help?
You need to implement the map delegate. The annotationViews are assigned there.
http://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapViewDelegate_Protocol/MKMapViewDelegate/MKMapViewDelegate.html
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
Try to create your own annotationView's class and call viewforannotation like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
if ([annotation isKindOfClass:[yourAnnotationClass class]]) {
yourAnnotationView *annotationView = (yourAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"annotation"];
if (!annotationView)
annotationView = [[yourAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annotation" delegate:self];
else
annotationView.annotation = annotation;
return annotationView;
}
return [_mapView viewForAnnotation:annotation];
}
Have you connected your _mapView IBOutlet to the MKMapView you have in your interface? Without it it doesn't know that the MKMapView it is drawing on screen is the one you are calling _mapView and adding annotations to, so it won't ask to draw those annotations.
Use this:
mapView=[[MKMapView alloc]initWithFrame:CGRectMake(0,0,320,460)];
mapView.userInteractionEnabled=YES;
mapView.zoomEnabled=YES;
mapView.delegate=self;
[self.view addSubview:mapView];
[self annotationPlacing];
// 36.777313,-119.706405 36.120838,-115.33682 47.798397,-121.519775 47.606337,-122.330618
}
-(void)annotationPlacing
{
NSArray *arrLat=[[NSArray alloc]initWithObjects:#"36.777313",#"36.120838",#"47.798397",#"47.606337", nil];
NSArray *arrLong=[[NSArray alloc]initWithObjects:#"-119.706405",#"-115.33682",#"-121.519775",#"-122.330618", nil];
for(int i=0;i<4;i++)
{
CLLocationCoordinate2D location;
location.latitude=[[arrLat objectAtIndex:i]floatValue];
location.longitude=[[arrLong objectAtIndex:i]floatValue];
MKPointAnnotation *annottion=[[MKPointAnnotation alloc]init];
annottion.coordinate=location;
annottion.title=#"India";
annottion.subtitle=#"Hyderabad";
[arrAnnotation addObject:annottion];
[mapView addAnnotation:annottion];
}
}
I have a pin with disclosure button, trying to add a detail view when the button is tapped.
I am using Apple's MapCallouts as reference. I'm getting the error
Application tried to push a nil view controller on target
using their code example -
[self.navigationController pushViewController:self.detailViewController animated:YES];
Am I implementing it in the wrong place? I dont know why Im getting the error.
having trouble linking everything such as #import, #class #interface #property #implementation #synthesize... don't know what does what.
-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:
(id <MKAnnotation>)annotation {
MKPinAnnotationView *pinView = nil;
if(annotation != mapView.userLocation)
{
static NSString *defaultPinID = #"com.invasivecode.pin";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil ) pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.canShowCallout = YES;
pinView.animatesDrop = YES;
// add a detail disclosure button to the callout which will open a new view controller page
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = rightButton;
}
else {
[mapView.userLocation setTitle:#"Current Location"];
}
return pinView;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
id<MKAnnotation> myAnnotation = [self.mapView.annotations objectAtIndex:0];
[self.mapView selectAnnotation:myAnnotation animated:YES];
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
// code to show details view
MapDetailViewController *detailViewController = [[MapDetailViewController alloc] init];
// Adds the above view controller to the stack and pushes it into view
[self.navigationController pushViewController:detailViewController animated:YES];
// We can release it again, because it's retained (and autoreleases in the stack). You can also choose to autorelease it when you allocate it in the first line of code, but make sure you don't call release on it then!
[MapDetailViewController release];
}