my app is showing the map in the right way, but the annotation (the pin of a place choosen by me) isn't displayed... why?
- (void)viewDidLoad{
[super viewDidLoad];
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = 45.40170;
zoomLocation.longitude = 8.91552;
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, METERS_PER_MILE, METERS_PER_MILE);
[_mappa setRegion:viewRegion animated:YES];
[_mappa regionThatFits:viewRegion];
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = zoomLocation;
point.title = #"TITLE";
point.subtitle = #"SUBTITLE";
[_mappa addAnnotation:point];
}
sorry but i'm new to xcode, for smartphone, i've developed only on android
Your code appears to add the annotation to the MKMapView correctly, but you need to provide an annotation view for the annotation by implementing delegate method mapView: viewForAnnotation: in your view controller, like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"MyLocation";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
Assuming you're using interface builder, make sure you set the MKMapView's delegate to your view controller.
Related
Sorry, I have read a bunch of tutorials how to create a custom Callout for MapKit Annotation. It works with NSLog, but I cannot display the information in the Callouts.
I have two type of icons on the map. This is my viewForAnnotation method:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if (! [annotation isKindOfClass:[IGAMapAnnotation class]]) {
return nil;
}
IGAMapAnnotation *myLocation = (IGAMapAnnotation *) annotation;
self.typeIsFix = [myLocation.navaidType isEqualToString:#"FIX"];
self.typeIsPort = [myLocation.navaidType isEqualToString:#"PORT"];
int planeImageViewTag = 42;
NSString *reuseId;
if (self.typeIsPort)
reuseId = #"IGAMapAnnotationPort";
else if (self.typeIsFix)
reuseId = #"IGAMapAnnotationFix";
else
reuseId = #"IGAMapAnnotationOther";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (annotationView == nil)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
annotationView.enabled = YES;
UIButton *annotationInfo = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = annotationInfo;
annotationView.canShowCallout = YES;
if (self.typeIsPort)
{
annotationView.image = [UIImage imageNamed:#"mapPORT.png"];
annotationView.centerOffset = CGPointMake(0, 0);
}
else if (self.typeIsFix)
{
annotationView.image = [UIImage imageNamed:#"mapFIX.png"];
annotationView.centerOffset = CGPointMake(0, 0);
}
else
return nil;
}
else
{
annotationView.annotation = annotation;
}
return annotationView;
}
Then I have a calloutAccessoryControlTapped method
- (void)mapView:(MKMapView *)mapview annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
IGAAnnotationInfoViewController *popOverCallout = [[IGAAnnotationInfoViewController alloc]init];
UIPopoverController *popOver = [[UIPopoverController alloc] initWithContentViewController:popOverCallout];
popOver.popoverContentSize = CGSizeMake(300, 200);
[popOver presentPopoverFromRect:view.bounds
inView:view
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
I have also created a UIViewController which I assigned to UIPopoverController.
Now, when I tap the button on my annotation I see a white space for text. Great. If I assign text to a label in UIViewController, it also works great (the following is my UIViewController.m):
- (void)viewDidLoad {
[super viewDidLoad];
txtCallout = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 300, 200) ];
txtCallout.font = [UIFont fontWithName:#"Arial Rounded MT Bold" size:(14.0)];
txtCallout.numberOfLines = 0;
txtCallout.clipsToBounds = YES;
txtCallout.backgroundColor = [UIColor clearColor];
txtCallout.textColor = [UIColor blackColor];
txtCallout.textAlignment = NSTextAlignmentLeft;
txtCallout.text = #"text\ntext\ntext";
[self.view addSubview:txtCallout];
}
But how do I insert the text from my annotation method? Also the text must be different depending on the icon type, say #"PORT, PORT" or #"FIX,FIX". How do I do it?
EDIT:
I have managed to display callouts with the necessary information passed. My last problem is that sometimes my callout is 3 lines, sometimes -- as many as 15. How is it possible to make the callout adjust automatically to the number of lines in my string? Should I modify my popoverContentSize or my label size in the UIViewController?
Thank you so much!
I have figured out how to adjust the MK Annotation callout to a UILabel.
Implement the calloutAccessoryControlTapped method
- (void)mapView:(MKMapView *)mapview annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// OPTIONAL: Deselecting Annotation View when Callout is tapped
//[mapview deselectAnnotation:view.annotation animated:YES];
NSString *calloutDetails;
IGAMapAnnotation *annotationTapped = (IGAMapAnnotation *)view.annotation;
calloutDetails = [NSString stringWithFormat:#"YOUR TEXT:\nYOURTEXT\n"];
// Declare and initialize the UIViewController that has the label to contain the callout information
IGAAnnotationInfoViewController *detailViewController = [[IGAAnnotationInfoViewController alloc]initWithText:calloutDetails];
UIPopoverController *popOver = [[UIPopoverController alloc] initWithContentViewController:detailViewController];
// Size of the UIPopoverController = size of the label + 40 pts
popOver.popoverContentSize = CGSizeMake(detailViewController.txtCallout.frame.size.width+40,detailViewController.txtCallout.frame.size.height+40);
// Show popover controller
[popOver presentPopoverFromRect:view.bounds
inView:view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
Now, IGAAnnotationInfoViewController.h
#interface IGAAnnotationInfoViewController : UIViewController {
CGRect calloutSize;
}
#property (strong,nonatomic) NSString *calloutInformation;
#property (strong,nonatomic) IGACalloutLabel *txtCallout;
-(IGAAnnotationInfoViewController*) initWithText : (NSString*) calloutText;
IGAAnnotationInfoViewController.m
#implementation IGAAnnotationInfoViewController
#synthesize calloutInformation,txtCallout;
-(IGAAnnotationInfoViewController*) initWithText : (NSString*) calloutText {
self = [super init];
if ( self ) {
calloutInformation = calloutText;
// Creating a label that will display the callout information (passed from IGAAcarasViewController - Map Annotation)
txtCallout = [[IGACalloutLabel alloc] initWithFrame:CGRectMake(20, 20, 0, 0)];
txtCallout.lineBreakMode = NSLineBreakByWordWrapping;
txtCallout.numberOfLines=0;
txtCallout.backgroundColor = [UIColor clearColor];
txtCallout.textColor=[UIColor blueColor];
txtCallout.text = calloutInformation;
[txtCallout drawTextInRect:CGRectMake(10,10,0,0)];
[txtCallout sizeToFit];
[self.view addSubview:txtCallout];
}
return self;
}
Finally, subclass the UILabel class:
implementation IGACalloutLabel
#synthesize topInset, leftInset, bottomInset, rightInset;
- (void)drawTextInRect:(CGRect)rect
{
UIEdgeInsets insets = {topInset,leftInset,bottomInset,rightInset};
return [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
Regards,
Hi how i can disable the autorotation for a view in a tabview controller??
i have testet to disable in a navigationclass, but thats not possible.
Thats my didFinishLaunching in the AppDelegate.m.
I hope everyone have an idea??
Thanks!
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Set the application defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:#"YES"
forKey:#"myKeyName"];
[defaults registerDefaults:appDefaults];
[defaults synchronize];
[self setupFetchedResultsController];
if (![[self.fetchedResultsController fetchedObjects] count] > 0 ) {
NSLog(#"!!!!! ~~> There's nothing in the database so defaults will be inserted");
[self importCoreDataDefaultRoles];
[self importCoreDataDefaultMaterials];
[self importCoreDataDefaultPersons];
}
else {
NSLog(#"There's stuff in the database so skipping the import of default data");
}
// TAB BAR
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
NSLog(#"I'm an iPad");
// *** Set up the Persons Split Views (2-Way Delegation & Pass Managed Object Context) *** //
// Set up SPLIT VIEW for Persons
UISplitViewController *splitViewController = [[tabBarController viewControllers] objectAtIndex:0];
// Set up Split View MASTER view for Persons
UINavigationController *personsMasterTVCnav = [splitViewController.viewControllers objectAtIndex:0];
splitViewController.delegate = (id)personsMasterTVCnav.topViewController;
PersonsTVC *personsTVC = [[personsMasterTVCnav viewControllers] objectAtIndex:0];
personsTVC.managedObjectContext = self.managedObjectContext;
// Set up Split View DETAIL view for Persons
UINavigationController *personsDetailTVCnav = [splitViewController.viewControllers objectAtIndex:1];
PersonDetailTVC *personDetailTVC = [personsDetailTVCnav.viewControllers objectAtIndex:0];
// Set up MASTER and DETAIL delegation so we can send messages between views
personsTVC.delegate = personDetailTVC;
personDetailTVC.delegate = personsTVC;
// *** Set up the Roles Views *** (Pass Managed Object Context)//
UINavigationController *rolesTVCnav = [[tabBarController viewControllers] objectAtIndex:1];
RolesTVC *rolesTVC = [[rolesTVCnav viewControllers] objectAtIndex:0];
rolesTVC.managedObjectContext = self.managedObjectContext;
// *** Set up the Materials Views *** (Pass Managed Object Context)//
UINavigationController *materialsTVCnav = [[tabBarController viewControllers] objectAtIndex:2];
MaterialsTVC *materialsTVC = [[materialsTVCnav viewControllers] objectAtIndex:0];
materialsTVC.managedObjectContext = self.managedObjectContext;
// Set delegate for splitViewController
splitViewController.delegate = personDetailTVC;
}
else
{
NSLog(#"I'm an iPhone or iPod Touch");
// The Two Navigation Controllers attached to the Tab Bar (At Tab Bar Indexes 0 and 1)
UINavigationController *personsTVCnav = [[tabBarController viewControllers] objectAtIndex:0];
UINavigationController *rolesTVCnav = [[tabBarController viewControllers] objectAtIndex:1];
UINavigationController *materialsTVCnav = [[tabBarController viewControllers] objectAtIndex:2];
// The Persons Table View Controller (First Nav Controller Index 0)
PersonsTVC *personsTVC = [[personsTVCnav viewControllers] objectAtIndex:0];
personsTVC.managedObjectContext = self.managedObjectContext;
// The Roles Table View Controller (Second Nav Controller Index 0)
RolesTVC *rolesTVC = [[rolesTVCnav viewControllers] objectAtIndex:0];
rolesTVC.managedObjectContext = self.managedObjectContext;
// The Materials Table View Controller (Third Nav Controller Index 0)
MaterialsTVC *materialsTVC = [[materialsTVCnav viewControllers] objectAtIndex:0];
materialsTVC.managedObjectContext = self.managedObjectContext;
}
return YES;
}
Disabling entire UIViewController auto-rotation
A UIViewController embedded in a UITabBarController is relying upon that last controller to handle the -supportedInterfaceOrientations messages. It is not ; basically the same problem as in this post: iOS 6 rotations: supportedInterfaceOrientations doesn´t work?
You must subclass your UITabBarController, and add this code to query each tab:
// In UITabBarController subclass
- (BOOL)shouldAutorotate;
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
UIViewController * top;
UIViewController * tab = self.selectedViewController;
if([tab isKindOfClass:
([UINavigationController class])]) {
top = [((UINavigationController *)tab)
topViewController];
}
if ([top respondsToSelector:#selector(supportedInterfaceOrientations)])
return [top supportedInterfaceOrientations];
else
return [super supportedInterfaceOrientations];
}
Of course, you must still respect the general auto-rotation rules and set the flags in the plist.
For each UIViewController subclass you want to prevent orientation changes, respond to supportedInterfaceOrientations as so:
// In UIViewController subclass
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
// Or whatever orientation you support
}
See this post for further details: Handling autorotation for one view controller in iOS7
Rotating a UIViewController except one or more subviews
Use this method:
-(void)counterRotateView:(UIView *)view
toInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
{
NSParameterAssert(view);
CALayer* layer = view.layer;
CABasicAnimation* animation;
animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
CGFloat tau = 0;
switch (toInterfaceOrientation) {
case UIInterfaceOrientationLandscapeLeft:
tau = 0.5; break;
case UIInterfaceOrientationLandscapeRight:
tau = -0.5; break;
case UIInterfaceOrientationPortraitUpsideDown:
tau = 1; break;
case UIInterfaceOrientationPortrait:
default:
break;
}
animation.toValue = [NSNumber numberWithFloat:tau * M_PI];
animation.duration = duration;
animation.cumulative = YES;
animation.repeatCount = 1;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[layer addAnimation:animation forKey:#"transform.rotation.z"];
}
And invoke it from here:
-(void) willRotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
{
[self counterRotateView:someView
toInterfaceOrientation:toInterfaceOrientation
duration:duration];
}
Here is the screen of my nib file and map view but the map is always taking the whole view.I tried resetting my simulator and cleaning my Xcode but nothing works.Am i doing something wrong?
Do i need to give mapview a frame programmatically?
http://prntscr.com/1sy1bw
I also want the button to be on top of mapview if the map cannot be set to take a particular frame.
Consider the below code please:
#import "mapViewController.h"
#interface mapViewController ()
#end
#implementation mapViewController
#synthesize mapView,source,dest,latdest,latsource,longdest,longsource;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
dest=#"delhi";
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0,0,160,240)];
CLGeocoder *geocoder1 = [[CLGeocoder alloc] init];
[geocoder1 geocodeAddressString:source
completionHandler:^(NSArray* placemarks, NSError* error)
{
for (CLPlacemark* aPlacemark in placemarks)
{
coordinate.latitude = aPlacemark.location.coordinate.latitude;
latsource=&coordinate.latitude;
coordinate.longitude = aPlacemark.location.coordinate.longitude;
longsource=&coordinate.longitude;
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
[annotation setCoordinate:(coordinate)];
[annotation setTitle:source];
annotation.subtitle = #"I'm here!!!";
mapView.delegate = self;
[self.mapView addAnnotation:annotation];
}
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
//mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0,0,160,240)];
CLGeocoder *geocoder1 = [[CLGeocoder alloc] init];
[geocoder1 geocodeAddressString:source
completionHandler:^(NSArray* placemarks, NSError* error)
{
for (CLPlacemark* aPlacemark in placemarks)
{
coordinate.latitude = aPlacemark.location.coordinate.latitude;
latsource=&coordinate.latitude;
coordinate.longitude = aPlacemark.location.coordinate.longitude;
longsource=&coordinate.longitude;
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
[annotation setCoordinate:(coordinate)];
[annotation setTitle:source];
annotation.subtitle = #"I'm here!!!";
[self.view addSubview:mapView];
[self.mapView addAnnotation:annotation];
}
}];
}
I commented the mapview alloc codeline and this fixed the issue.Seems like mapview gets pushed as soon as its allocated memory space.Moreover the frame coordinated that i initialised mapviewview with are still preserved although the code is commented.Dont know if it should work this way only or not but it did solved my issue .
I am trying to find a way to make multiple annotation disclosure buttons open other views.
For example, annotation1 disclosure button needs to open ViewController 1.xib, annotation2 needs to open ViewController2.xib, and so on. Is this possible?
I've got this code so far, which sets the location according to coordinates, and sets the user location.
- (void)viewDidLoad
{
[super viewDidLoad];
_mapView.showsUserLocation = YES;
CLLocationCoordinate2D annotationCoord;
annotationCoord.latitude = 40.714353;
annotationCoord.longitude = -74.005973;
coord = 1;
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init];
annotationPoint.coordinate = annotationCoord;
annotationPoint.title = #"New York";
[_mapView addAnnotation:annotationPoint];
CLLocationCoordinate2D annotationCoord1;
annotationCoord1.latitude = 51.511214;
annotationCoord1.longitude = -0.119824;
coord = 2;
MKPointAnnotation *annotationPoint1 = [[MKPointAnnotation alloc] init];
annotationPoint1.coordinate = annotationCoord1;
annotationPoint1.title = #"London";
[_mapView addAnnotation:annotationPoint1];
}
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView *mapPin = nil;
if(annotation != map.userLocation)
{
static NSString *defaultPinID = #"defaultPin";
mapPin = (MKPinAnnotationView *)[map dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if (mapPin == nil )
{
mapPin = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:defaultPinID] autorelease];
mapPin.canShowCallout = YES;
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
mapPin.rightCalloutAccessoryView = infoButton;
}
else
mapPin.annotation = annotation;
}
return mapPin;
}
What do I put into here:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// ???
}
All help is appreciated. Thanks
You could do something like this,
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
if ([view.annotation.title isEqualToString:#"The titel of your annotation"])
{
// Do something
}
}
I have a problem with the PIn color mapView when a refresh is done.
In my I app i display some point with two color in order to identify if a service is available.
On the first start, no problems appear. The code is the follower:
- (void)viewDidLoad
{
[super viewDidLoad];
[self dowloadPoint]; // here I exucte the first start
}
- (void)dowloadPoint{
NSURL *url1 =[NSURL URLWithString:#"http:MYUSRL"];
NSData *datos1 =[[NSData alloc] initWithContentsOfURL:url1];
[self plotBarPosition:datos_string1]; //Here I call the plotBarPosition method
}
- (void)plotBarPosition:(NSString *)datos_string1 {
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
// Parse the string into JSON
NSDictionary *json = [(NSDictionary*)[datos_string1 JSONValue]objectForKey:#"features"];
// Get the objects you want, e.g. output the second item's client id
NSArray *items_properties = [json valueForKeyPath:#"properties"];
NSArray *items_geo = [json valueForKeyPath:#"geometry"];
for (int i = 0; i < [json count]; i++){
NSString *nomprePunto =[[items_properties objectAtIndex:i] objectForKey:#"title"];
NSNumber *lat =[[[items_geo objectAtIndex:i] objectForKey:#"coordinates"] objectAtIndex:0];
NSNumber *lon =[[[items_geo objectAtIndex:i] objectForKey:#"coordinates"] objectAtIndex:1];
CLLocationCoordinate2D coordinate;
coordinate.latitude = lat.doubleValue;
coordinate.longitude = lon.doubleValue;
//ESTADO
NSString *description = [[items_properties objectAtIndex:i] objectForKey:#"description"];
NSString *estado_punto = [[NSString alloc]init];
if ([description rangeOfString:#"Averiado"].location == NSNotFound) {
estado_punto = #"Available";
} else {
estado_punto = #"NOt Available";
averiados ++;
}
NSString *averiadosStr = [NSString stringWithFormat:#"%d",averiados];
averiadosLabel.text = averiadosStr;
MyLocation *location =[[MyLocation alloc] initWithName:nomprePunto coordinate:coordinate estado:estado_punto];
[_mapView addAnnotation:location];
}
}
- (MKPinAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(MyLocation *)annotation {
static NSString *identifier = #"MyLocation";
if ([annotation isKindOfClass:[MyLocation class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
if([[annotation estado] isEqualToString:#"En Servicio"])
annotationView.pinColor = MKPinAnnotationColorGreen;
} else {
annotationView.annotation = annotation;
}
return annotationView;
}
return nil;
}
But whe I add a refres button that is function is simply a refreshcalling the dowloadPoint once again,
- (IBAction)refresh{
[self dowloadPoint];
}
the color of pins change in a "random manner", not corrisponding with the real state of point.
Any ideas about what is happening? Thanks in advance.
EDIT: It seemps pproblems is due to:
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
erasing it, the app work properly but pins area drown abow the previous ones...:S
The default color of the pin is red. You set it to green if the estado property of your MyLocation object is equal to #"En Servicio". I understand that sometimes the color is red, when your estado property is equal to #"En Servicio", or sometimes green when it is not.
One reason could be that your MyLocation object simply does no longer exist when you press the refresh button. In this case, you might still have a pointer to the memory location where it once existed, but this location may have been overwritten by anything, causing a random color.
This can happen e.g. if your MyLocation object has been created as an autorelease object that has been released when you returned to the main event loop, i.e. to handle user interactions.
This should not be the case if you are using ARC.