MKAnnotation viewForAnnotation never called - iphone

I have already add UIViewController<MKMapViewDelegate> in .h and already add
-(void) viewDidLoad {
self.mapView.delegate = self;
}
but the method viewForAnnotation never called
MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface Tela1ViewController : UIViewController<MKMapViewDelegate> {
IBOutlet MKMapView *mapView;
}
#property (nonatomic, retain) MKMapView *mapView;
#end
MapViewController.m
-(void) viewDidLoad {
self.mapView.delegate = self;
}
- (void)viewDidAppear:(BOOL)animated
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication shared
mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
mapView.mapType = MKMapTypeSatellite;
CLLocationCoordinate2D coord = {latitude: appDelegate.latitude, longitude: appDelegate.longitude};
MKCoordinateSpan span = {latitudeDelta:0.2, longitudeDelta: 0.2};
MKCoordinateRegion region = {coord, span};
[mapView setRegion:region];
PlaceMark *addAnnotation = [[PlaceMark alloc] initWithCoordinate:coord];
[mapView addAnnotation:addAnnotation];
[self.view addSubview:mapView];
}
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"test");
}

Okay, couple of things to fix in your code ...
Delegate of MKMapView
Is nil. Why? Because you set delegate of MKMapView in viewDidLoad method. But when you look at viewDidAppear:, you're allocating new MKMapView and you're not setting delegate there. And because viewDidLoad is called before viewDidAppear:, delegate is simply nil = not set.
Calling super
When you override some methods, read documentation. Because you can find this in viewDidAppear: documentation for example:
You can override this method to perform additional tasks associated with presenting the view. If you override this method, you must call super at some point in your implementation.
MKMapView & IBOutlet
And also don't understand why do you have MKMapView as IBOutlet and then you're allocating new MKMapView and adding it as subview. Also if your IBOutlet is really connected to a MKMapView in your XIB, you'll end up with two MKMapViews, because the old one (from XIB) is not removed from superview.
You're clearly messing things up. Go and read more about UIKit, ...

Try replacing the last several lines of you viewDidAppear method with the following:
CLLocationCoordinate2D coord = {.latitude = location.latitude, .longitude = location.longitude};
MKCoordinateSpan span = {.latitudeDelta = 0.2, .longitudeDelta = 0.2};
MKCoordinateRegion region = {coord, span};
[mapView setRegion:region];
MapAnnotation *addAnnotation = [[MapAnnotation alloc] initWithCoordinate:coord];
[mapView addAnnotation:addAnnotation];
[self.view addSubview:mapView];

Related

How to add 'Maps' app link to each of my map annotations

There are a few tutorials and questions on this, but I'm not knowledgeable enough yet to understand how to implement them into my particular app. I get JSON annotation data from a URL and parse it and add each annotation in for loop. I want to add a link on each annotation to open Maps for directions.
Here's my ViewController.H
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#import <MapKit/MapKit.h>
//MAP Setup
#interface ViewController : UIViewController <MKMapViewDelegate>
//map setup
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (nonatomic, strong) NSMutableData *downloadData;
//- (IBAction)refreshTapped:(id)sender;
#end
and my ViewController.m
- (void)viewDidLoad
{
////////////////////////
//Connection to download JSON map info
////////////////////////
self.downloadData = [NSMutableData new];
NSURL *requestURL2 = [NSURL URLWithString:#"http:OMITTED"];
NSURLRequest *request = [NSURLRequest requestWithURL:requestURL2];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
//scroller
[scroller setScrollEnabled:YES];
[scroller setContentSize:CGSizeMake(320,900)];
[super viewDidLoad];
//Map
[self.mapView.userLocation addObserver:self
forKeyPath:#"location"
options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld)
context:nil];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.downloadData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
id parsed = [NSJSONSerialization JSONObjectWithData:_downloadData options:kNilOptions error:nil];
////////////////////////
//Iterating and adding annotations
////////////////////////
for (NSDictionary *pointInfo in parsed)
{
NSLog([pointInfo objectForKey:#"name"]);
double xCoord = [(NSNumber*)[pointInfo objectForKey:#"lat"] doubleValue];
double yCoord = [(NSNumber*)[pointInfo objectForKey:#"lon"] doubleValue];
CLLocationCoordinate2D coords = CLLocationCoordinate2DMake(xCoord, yCoord);
MKPointAnnotation *point = [MKPointAnnotation new];
point.coordinate = coords;
point.title = [pointInfo objectForKey:#"name"];
[self.mapView addAnnotation:point];// or whatever your map view's variable name is
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//centers map on user loc and then allows for movement of map without re-centering on userlocation check.
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if ([self.mapView showsUserLocation])
{
MKCoordinateRegion region;
region.center = self.mapView.userLocation.coordinate;
MKCoordinateSpan span;
span.latitudeDelta = .50; // Change these values to change the zoom
span.longitudeDelta = .50;
region.span = span;
[self.mapView setRegion:region animated:YES];
self.mapView.showsUserLocation = NO;}
}
- (void)dealloc
{
[self.mapView.userLocation removeObserver:self forKeyPath:#"location"];
[self.mapView removeFromSuperview]; // release crashes app
self.mapView = nil;
}
#end
Launching the Maps App of the Location Awareness Programming Guide says:
If you would prefer to display map information in the Maps app as opposed to your own app, you can launch Maps programmatically using one of two techniques:
In iOS 6 and later, use an MKMapItem object to open Maps.
In iOS 5 and earlier, create and open a specially formatted map URL as described in Apple URL Scheme Reference.
The preferred way to open the Maps app is to use the MKMapItem class. This class offers both the openMapsWithItems:launchOptions: class method and the openInMapsWithLaunchOptions: instance method for opening the app and displaying locations or directions.
For an example showing how to open the Maps app, see “Asking the Maps App to Display Directions.”
So, you should:
Make sure to define your view controller to be the delegate for your map view;
Write a viewForAnnotation that turns on canShowCallout and turns on the callout accessory view:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
MKAnnotationView* annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"MyCustomAnnotation"];
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return annotationView;
}
Then write a calloutAccessoryControlTapped method that opens the maps as outlined above, based upon what versions of iOS you're supporting, e.g., for iOS 6:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
id <MKAnnotation> annotation = view.annotation;
CLLocationCoordinate2D coordinate = [annotation coordinate];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate addressDictionary:nil];
MKMapItem *mapitem = [[MKMapItem alloc] initWithPlacemark:placemark];
mapitem.name = annotation.title;
[mapitem openInMapsWithLaunchOptions:nil];
}
I don't know what additional geographic information your have in your KML, but you can presumably fill in the addressDictionary as you see fit.
In answer to your follow-up question about how to use the addressDictionary parameter of the MKPlacemark initializer method, initWithCoordinate, if you had NSString variables for the street address, the city, the state, the zip, etc., it would look like:
NSDictionary *addressDictionary = #{(NSString *)kABPersonAddressStreetKey : street,
(NSString *)kABPersonAddressCityKey : city,
(NSString *)kABPersonAddressStateKey : state,
(NSString *)kABPersonAddressZIPKey : zip};
For this to work, you have to add the appropriate framework, AddressBook.framework, to your project and import the header in your .m file:
#import <AddressBook/AddressBook.h>
The real question, though, was how to set the name for the MKMapItem so it doesn't show up as "Unknown Location" in the maps app. That's as simple as setting the name property, probably just grabbing the title from your annotation:
mapitem.name = annotation.title;

Adding pins to MapView

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.

How to add annotation on center of map view in iPhone?

I have MAP view in my app and i want to add Annotation pin (Red pin) on center of map view.
Now when user scroll map view pin should adjust with center according to that.
How to do that?
Thank
If you want to use an actual annotation instead of just a regular view positioned above the center of the map view, you can:
use an annotation class with a settable coordinate property (pre-defined MKPointAnnotation class eg). This avoids having to remove and add the annotation when the center changes.
create the annotation in viewDidLoad
keep a reference to it in a property, say centerAnnotation
update its coordinate (and title, etc) in the map view's regionDidChangeAnimated delegate method (make sure map view's delegate property is set)
Example:
#interface SomeViewController : UIViewController <MKMapViewDelegate> {
MKPointAnnotation *centerAnnotation;
}
#property (nonatomic, retain) MKPointAnnotation *centerAnnotation;
#end
#implementation SomeViewController
#synthesize centerAnnotation;
- (void)viewDidLoad {
[super viewDidLoad];
MKPointAnnotation *pa = [[MKPointAnnotation alloc] init];
pa.coordinate = mapView.centerCoordinate;
pa.title = #"Map Center";
pa.subtitle = [NSString stringWithFormat:#"%f, %f", pa.coordinate.latitude, pa.coordinate.longitude];
[mapView addAnnotation:pa];
self.centerAnnotation = pa;
[pa release];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
centerAnnotation.coordinate = mapView.centerCoordinate;
centerAnnotation.subtitle = [NSString stringWithFormat:#"%f, %f", centerAnnotation.coordinate.latitude, centerAnnotation.coordinate.longitude];
}
- (void)dealloc {
[centerAnnotation release];
[super dealloc];
}
#end
Now this will move the annotation but not smoothly. If you need the annotation to move more smoothly, you can add a UIPanGestureRecognizer and UIPinchGestureRecognizer to the map view and also update the annotation in the gesture handler:
// (Also add UIGestureRecognizerDelegate to the interface.)
// In viewDidLoad:
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
panGesture.delegate = self;
[mapView addGestureRecognizer:panGesture];
[panGesture release];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
pinchGesture.delegate = self;
[mapView addGestureRecognizer:pinchGesture];
[pinchGesture release];
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
centerAnnotation.coordinate = mapView.centerCoordinate;
centerAnnotation.subtitle = [NSString stringWithFormat:#"%f, %f", centerAnnotation.coordinate.latitude, centerAnnotation.coordinate.longitude];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
//let the map view's and our gesture recognizers work at the same time...
return YES;
}
Anna's answer is clever approach to keeping an annotation centered in the map using the annotations in the standard mapview way. However, as one commenter pointed out, the scrolling and pinching while much better still shows noticeable lag, and the recommended approach would be to add the annotation view as a subview of the mapview. Here's what that looks like.
#interface SHCenterPinMapViewController () <MKMapViewDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (strong, nonatomic) MKPointAnnotation *centerAnnotaion;
#property (strong, nonatomic) MKPinAnnotationView *centerAnnotationView;
#end
#implementation SHCenterPinMapViewController
- (MKPointAnnotation *)centerAnnotaion
{
if (!_centerAnnotaion) {
_centerAnnotaion = [[MKPointAnnotation alloc] init];
}
return _centerAnnotaion;
}
- (MKPinAnnotationView *)centerAnnotationView
{
if (!_centerAnnotationView) {
_centerAnnotationView = [[MKPinAnnotationView alloc] initWithAnnotation:self.centerAnnotaion
reuseIdentifier:#"centerAnnotationView"];
}
return _centerAnnotationView;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.delegate = self;
[self.mapView addSubview:self.centerAnnotationView];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self moveMapAnnotationToCoordinate:self.mapView.centerCoordinate];
}
// These are the constants need to offset distance between the lower left corner of
// the annotaion view and the head of the pin
#define PIN_WIDTH_OFFSET 7.75
#define PIN_HEIGHT_OFFSET 5
- (void)moveMapAnnotationToCoordinate:(CLLocationCoordinate2D) coordinate
{
CGPoint mapViewPoint = [self.mapView convertCoordinate:coordinate toPointToView:self.mapView];
// Offset the view from to account for distance from the lower left corner to the pin head
CGFloat xoffset = CGRectGetMidX(self.centerAnnotationView.bounds) - PIN_WIDTH_OFFSET;
CGFloat yoffset = -CGRectGetMidY(self.centerAnnotationView.bounds) + PIN_HEIGHT_OFFSET;
self.centerAnnotationView.center = CGPointMake(mapViewPoint.x + xoffset,
mapViewPoint.y + yoffset);
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
self.centerAnnotaion.coordinate = mapView.centerCoordinate;
[self moveMapAnnotationToCoordinate:mapView.centerCoordinate];
}
#end
Really the only interesting thing to not is that you have to offset the MKPinAnnotationView by a certain amount to account for the distance between the lower left corner and the pin head. I don't like having these asset dependent constants in the code, so if anyone can find a better way to do that, I am all ears.
I created a github project with a map controller that does this as well as some other things related to using a mapview to have a user select a location. Check it out here: https://github.com/scottrhoyt/CenterPinMapViewController
Swift version
In class:
var centerAnnotation = MKPointAnnotation()
var centerAnnotationView = MKPinAnnotationView()
In viewDidLoad:
if #available(iOS 9.0, *) {
centerAnnotationView.pinTintColor = customColor
} else {
// Fallback on earlier versions
centerAnnotationView.pinColor = MKPinAnnotationColor.Red
}
self.view.addSubview(centerAnnotationView)
In viewDidAppear:
self.moveMapAnnotationToCoordinate(self.mapView.centerCoordinate)
Then:
func moveMapAnnotationToCoordinate(locate : CLLocationCoordinate2D ) {
let mapViewPoint : CGPoint = self.mapView.convertCoordinate(locate, toPointToView: self.mapView)
let pinWidth : CGFloat = 7.75
let pinHeight : CGFloat = 7
let xOffset : CGFloat = CGRectGetMidX(self.centerAnnotationView.bounds) - pinWidth
let yOffset : CGFloat = CGRectGetMidY(self.centerAnnotationView.bounds) - pinHeight
self.centerAnnotationView.center = CGPointMake(mapViewPoint.x - xOffset, mapViewPoint.y - yOffset)
}
For using change in location, add delegate CLLocationManagerDelegate and then:
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
self.centerAnnotation.coordinate = self.mapView.centerCoordinate
self.moveMapAnnotationToCoordinate(self.mapView.centerCoordinate)
}

Display annotation in Map kit

I am working with Mapkit and I have to show annotations in the map but I'm not able to display the annotation. Here's my code:
#interface MyMapView : UIViewController <MKAnnotation,MKMapViewDelegate>{
MKMapView *Obj_Map_View;
MKPlacemark *pmark;
MKReverseGeocoder *geocoder1;
}
#end
#import "MyMapView.h"
#implementation MyMapView
- (id)init {
if (self = [super init]) {
}
return self;
}
- (void)loadView {
[super loadView];
Obj_Map_View = [[MKMapView alloc]initWithFrame:self.view.bounds];
Obj_Map_View.showsUserLocation =YES;
Obj_Map_View.mapType=MKMapTypeStandard;
[self.view addSubview:Obj_Map_View];
Obj_Map_View.delegate = self;
CLLocationCoordinate2D cord = {latitude: 19.120000, longitude: 73.020000};
MKCoordinateSpan span = {latitudeDelta:0.3, longitudeDelta:0.3};
MKCoordinateRegion reg= {cord,span};
[Obj_Map_View setRegion:reg animated:YES];
//[Obj_Map_View release];
}
- (NSString *)subtitle{
return #"Sub Title";
}
- (NSString *)title{
return #"Title";
}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
MKPinAnnotationView *annov = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"Current location"];
annov.animatesDrop = TRUE;
[annotation title]==#"Current location";
annov.canShowCallout = YES;
[annov setPinColor:MKPinAnnotationColorGreen];
return annov;
}
The above code works fine and displays a map but not with annotation.
Typically, the class that conforms to the MKAnnotation protocol isn't the view controller, it's a data class.
You'll need to create another class, which I'll call "MyLandmarks" for the example.
#interface MyLandmarks : NSObject <MKAnnotation>
// Normally, there'd be some variables that contain the name and location.
// And maybe some means to populate them from a URL or a database.
// This example hard codes everything.
#end
#implementation MyLandmarks
-(NSString*)title {
return #"'ere I am, J.H.";
}
-(NSString*)subtitle {
return #"The ghost in the machine.";
}
-(CLLocationCoordinate2D) coordinate {
CLLocationCoordinate2D coord = {latitude: 19.120000, longitude: 73.020000};
return coord;
}
#end
Then, somewhere appropriate in your MyMapView class add:
MyLandmark *landmark = [[[MyLandmark alloc]init]autorelease];
[Obj_Map_View addAnnotation:landmark];
A couple other bits that other Objective-C developers working with you will appreciate:
To avoid confusion, don't call the class MyMapView if it descends from a UIViewController. Call it MyMapViewController, instead.
Classes start with a capital letter, variables start lowercase. Both are CamelCased. Obj_Map_View should be objMapView.
To add annotation use : addAnnotation:
read about it here

initializing the mapkit with a particular latitude and longitude Iphone

Iam making an iphone app, which should take in latitude and longitudes from an array and should locate and display the map with a customized annotation/pins. I have used the mapkit here's how:
//MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MapViewController : UIViewController <MKMapViewDelegate> {
IBOutlet MKMapView *mapView;
MKPlacemark *mPlacemark;
}
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
#end
//MapViewController.m
#import "MapViewController.h"
#import <MapKit/MapKit.h>
#implementation MapViewController
#synthesize mapView;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
CLLocationCoordinate2D location = mapView.userLocation.coordinate;
MKCoordinateRegion region;
MKCoordinateSpan span;
location.latitude = 37.250556;
location.longitude = -96.358333;
span.latitudeDelta = 0.05;
span.longitudeDelta = 0.05;
region.span = span;
region.center = location;
[mapView setRegion:region animated:YES];
[mapView regionThatFits:region];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
[mapView setNeedsDisplay];
}
#end
This does not locate the place for the latitude and longitude. Please help.
I have made a sample project with your code and it works fine - map gets positioned to the expected (it seems) region. Check if your mapView ivar is initialized (are connections set properly in IB?)
Edit: In your code you just set map's visible region but do not add any annotations to it (apart from automatically showing current user position which is always in Cupertino if you test on simulator). To put pin to you map you need to create an object confirming to MKAnnotation protocol and add it to mapView:
// Sample example just to show annotation
// in your program you will likely need to use custom annotation objects
CLLocation* myLocation = [[CLLocation alloc] initWithLatitude:37.250556 longitude:-96.358333];
[mapView addAnnotation:myLocation];
Some (not so relevant) comments on your code:
You don't need to initialize location variable with current location as you overwrite its coordinate values immediately after that
Why do you call [mapView setNeedsDisplay]; in viewDidUnload method? I'm not sure if this may cause serious problems but you must use this method for cleaning memory up (e.g. releasing retained outlets), not for redrawing your UI
Have you set your map to follow user location somewhere else? maybe that default is overruling your manual movement of the map to Kansas.
edit
Do'h, didn't see this was July last year. Well maybe you could share your solution with us if this wasn't the problem