MKMapView (annotations & locations) - iPhone development - iphone

How can I determine the coordinates of a certain spot?
How can I create a pin for 1 specific location after knowing it's coordinates? Is creating a new class for that necessary?
ex- PIN to : latitude = 37.786996;
longitude = -122.440100;

To add a basic pin at a given coordinate, the simplest way in iOS 4+ is to use the pre-defined MKPointAnnotation class (no need to define your own class) and call addAnnotation:.
If you need to define your own annotation class because you need some custom properties, don't use the classes shown in the MapCallouts sample app as a basis. They give the false impression that you need to define a separate class for each unique coordinate. Instead, create a class that implements the MKAnnotation protocol but with a settable coordinate property.
Edit:
An example with MKPointAnnotation:
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
annotation.coordinate = CLLocationCoordinate2DMake(33, 35);
annotation.title = #"Title";
annotation.subtitle = #"Subtitle";
[mapView addAnnotation:annotation];
[annotation release];
If you need to easily access the annotation in other methods in the class, you can make it an ivar instead.

You implement an object that supports the MKMapAnnotation protocol. This object you add to your mkmapview with the "addAnnotation:" call. This will give you the default pin on the map.
If you want to customize the pin you need to implement the MKMapViewDelegate protocol to return an MKAnnotationView with a custom image.

Related

MapKit - iOS 13 - How to show pin annotations on top on the user location annotation?

Currently, when I try to add an annotation to the map in the user's location the annotation is not showing up. When I set the annotation (using the same code) besides the user's location it does show up.
The code:
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate // set coordinate
annotation.title = "Title"
annotation.subtitle = "Subtitle"
mapView.addAnnotation(annotation)
same issue here, it was working fine with iOS 12, now all annotations near the current location marker are not visible anymore. Critical information might be near the user location and needs to be displayed. This is mostly bad when you zoom out the map, as you don't know anymore if there is data since all annotations comes near the user location...
Is there a document that indicates this change from apple? I didn't find any... thanks in advance for someone that finds it.
If there is no options to turn off this behavior, the only workaround I can think of is to manually add the current location annotation ourselves...
In the callback -mapView:viewForAnnotation on your MKMapViewDelegate be sure that the MKAnnotationView you return leaves its displayPriority as its default value which is MKFeatureDisplayPriorityRequired otherwise MapKit clusters your annotation view with the user location.
I have a fix for those having issues. I was having issues in my production app as well because I was just adding the annotations to the map and never calling the viewForAnnoation method. I created a sample project and uploaded it to Github so you can review the code and see what I did. In short, you need to create a class that subclasses NSObject and conforms to the MKAnnotation protocol. If using MKLocalsearch like my project, pass the MKMapItem object back and create a new instance of the new class. You will also need to create a class and subclass MKMarkerAnnotationView.
class CustomMKMarkerSubclass: MKMarkerAnnotationView {
override var annotation: MKAnnotation? {
willSet {
if let _ = newValue as? MapLocation {
displayPriority = MKFeatureDisplayPriority.required
canShowCallout = true
}
}
}
}
For more detail, check the project below.
MapKit Demo Project
Using MKPinAnnotationView instead of MKAnnotationView has fixed my issue
- (MKAnnotationView *)mapView:(MKMapView *)tempMapView viewForAnnotation:(id <MKAnnotation>)tempAnnotation{
if ([tempAnnotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
NSString *identifer = #"Identifer";
MKPinAnnotationView *dPV = [[MKPinAnnotationView alloc] initWithAnnotation:tempAnnotation reuseIdentifier:identifer];
[dPV setSelected:YES];
[dPV setTag:102];
dPV.canShowCallout = YES;
[dPV setSelected:YES];
dPV.draggable = YES;
return dPV;}
It seems like iOS 14 fixed this issue

How to display and connect multiple locations with a route with annotations in an MKMapView inside an iPhone App?

I need to display a MKMapView with more than 4 locations with different Annotations and a route connecting the locations. I have tried to display the multiple locations inside a MKMapView but i still not able to find out on how to connect the locations with a route. I am also trying to get this checked if i have implemented it in a right way. I have created a "CLLocationCoordinate2D" and then added a lat and long similarly for 4 points. I have created a custom object which implements MKAnnotation and returning a location .
CLLocationCoordinate2D coordinate1 = CLLocationCoordinate2DMake(40.7180583 ,-74.007109);
CLLocationCoordinate2D coordinate2 = CLLocationCoordinate2DMake(40.716355 ,-74.006816);
CLLocationCoordinate2D coordinate3 = CLLocationCoordinate2DMake(40.715281 ,-74.005485);
CLLocationCoordinate2D coordinate4 = CLLocationCoordinate2DMake(40.71559 ,-74.003114);
AnnotationPoints *location1 = [[AnnotationPoints alloc] initWithCoordinate:coordinate1];
AnnotationPoints *location2 = [[AnnotationPoints alloc] initWithCoordinate:coordinate2];
AnnotationPoints *location3 = [[AnnotationPoints alloc] initWithCoordinate:coordinate3];
AnnotationPoints *location4 = [[AnnotationPoints alloc] initWithCoordinate:coordinate4];
NSArray *poiArray = [[NSArray alloc] initWithObjects:location1,location2,location3,location4,nil];
[mapView addAnnotations:poiArray];
//Inside the Annotation Class initWithCoordinate Method is implemented this way:-
-(id)initWithCoordinate:(CLLocationCoordinate2D) c{
coordinate=c;
NSLog(#"%f,%f",c.latitude,c.longitude);
return self;
}
My concern here is i need to create a Annotation Point for every Location. Is there any alternative that i can load all the points at a single place. And another difficulty here is the route connecting all the multiple points. Any help on this? Thanks a lot
The way you are adding the annotations is fine.
Not sure what your concern is and what you mean by "all the points at a single place".
If you want pins/annotations at several places, you have to create a separate annotation object for each place.
Drawing a route connecting those locations requires creating an overlay (not an "annotation").
You want to add an MKPolyline to the map for which you will specify the list of coordinates.
To draw the polyline, you don't need to also add annotations at each coordinate (but you could if you want to).
Creating and adding an MKPolyline and its corresponding MKPolylineView is very similar to MKPolygon and MKPolygonView. See this question for an example:
iPhone MKMapView - MKPolygon Issues

MKMapView annotations changing/losing order?

I have a map view with annotations, and these annotations display a callout. When the callout's disclosure detail button is clicked, it segues into a new view.
My MKAnnotations are a custom class that implements <MKAnnotation>. Let's call that class MyClass. They are stored in an NSMutableArray. During viewdidload of this view, I add each object of MyClass in this array to the map view's annotations. Using the debugger, I can see that once all of this adding is done, the [self.MapView annotations] order is the same as the NSMutableArray.
Now I set another breakpoint within mapView:viewForAnnotation: and check out the order of 1) my NSMutableArray and 2) [self.MapView annotations]. The array is of course in the same order. However, the order of the annotations has been scrambled.
This was a big problem for me, because I needed to use the specific instance of MyClass that the user selected in the next view. AKA, I wanted to look at the annotation, find its index, and then use that to get the same index within the array.
I've now realized that I can just save the annotation directly (coming from an Android background, this was very cool to me). However, I am still conceptually at a loss as to why the order became scrambled. Can someone help me? Code below:
- (void)viewDidLoad
{
if([fromString isEqualToString:#"FromList"])
self.navigationItem.hidesBackButton = TRUE;
else {
self.navigationItem.rightBarButtonItem = nil;
}
self.array = [MySingleton getArray];
//set up map
//declare latitude and longitude of map center
CLLocationCoordinate2D center;
center.latitude = 45;
center.longitude = 45;
//declare span of map (height and width in degrees)
MKCoordinateSpan span;
span.latitudeDelta = .4;
span.longitudeDelta = .4;
//add center and span to a region,
//adjust the region to fit in the mapview
//and assign to mapview region
MKCoordinateRegion region;
region.center = center;
region.span = span;
MapView.region = [MapView regionThatFits:region];
for(MyClass *t in self.array){
[MapView addAnnotation:t];
}
[super viewDidLoad];
}
//this is the required method implementation for MKMapView annotations
- (MKAnnotationView *) mapView:(MKMapView *)thisMapView
viewForAnnotation:(MyClass *)annotation
{
static NSString *identifier = #"MyIdentifier";
//the result of the call is being cast (MKPinAnnotationView *) to the correct
//view class or else the compiler complains
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[thisMapView
dequeueReusableAnnotationViewWithIdentifier:identifier];
if(annotationView == nil)
{
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
}
annotationView.pinColor = MKPinAnnotationColorGreen;
//pin drops when it first appears
annotationView.animatesDrop=TRUE;
//tapping the pin produces a gray box which shows title and subtitle
annotationView.canShowCallout = YES;
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = infoButton;
return annotationView;
}
When you call addAnnotation or addAnnotations, the map view adds the reference(s) to its internal list of annotations.
The annotations property of MKMapView simply returns this internal list (whatever type it might be) as an NSArray.
I don't know of any place in the documentation where it states that the annotations property returns the array in the same order that you added the annotations in. If you have showsUserLocation turned on, the array will include that annotation even though you didn't explicitly add it.
You do not need to be concerned about nor should you depend on the order of the objects in the annotations property.
Just a few suggestions regarding the code:
Since your array contains objects that implement <MKAnnotation>, instead of looping through it, you can add all the annotations in one shot by calling addAnnotations (plural) and pass it the array
In viewForAnnotation, none of the properties you are setting depend on any specific annotation so you can set them all inside the if (av == nil) block. This way you get maximum reuse.
Also in viewForAnnotation, after and outside the if, you should set the annotation property of the view to the current annotation. This is in case the view is being reused from another annotation.
Finally, in viewForAnnotation, don't assume the annotation will be of type MyClass. If you turn on showsUserLocation, that won't be the case. It's safer to declare the parameter as id<MKAnnotation> and then if necessary check what its class is and then cast it.
#Anna, you state you should not be concerned for the order of the annotations. That's not true in my case. Some annotationviews might overlap, and I always need a specific one to be on the top of the two overlapping views. So the order DO makes sense for the annotations, as I hope the - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation gets called in the same order as i added the annotations.
EDIT:
and the solution is here :-)

issue with with MKAnnotation image property

I am implementing a MKMapView based application. In that I am using a custom image instead of displaying a pin by using MKAnnotationView.Image property. It is not working. It keeps showing the standard pin in red but not the image I am pointing too. Any ideas?
- (void)setPin:(MKPinAnnotationView *)aPin forAnnotation:(id <MKAnnotation>)anAnnotation {
if (anAnnotation == self.userAnnotation) {
aPin.image = [UIImage imageNamed:#"youarehere.png"];
aPin.opaque = NO;
aPin.pinColor = MKPinAnnotationColorRed;
}
}
If you use MKPinAnnotationView like you are then you don't get to set the image only the pinColor. MKPinAnnotationView only has pinColor and animatesDrop as the properties.
You want to use the parent class MKAnnotationView. It will let you set the image.
Read the Class Reference for each and it will make sense. This is one of the times that Objective-C makes me crazy because when you are reading those long class names in the docs, it's easy to scan right over the "Pin" variation in the class name.

How to open call out MKAnnotationView programmatically? (iPhone, MapKit)

I want to open up the callout for an MKPinAnnotationView programmatically. Eg I drop 10 pins on the map, and want to open up the one closest to me. How would I go about doing this?
Apple has specified the 'selected' parameter for MKAnnotationView's, but discourages setting it directly (this doesn't work, tried it).
For the rest MKAnnotationView only has a setHighlighted (same story), and can ShowCallout method..
Any hints if this is possible at all?
In your mapViewController create an action method:
- (void)openAnnotation:(id)annotation
{
//mv is the mapView
[mv selectAnnotation:annotation animated:YES];
}
You can then determine the closest annotation based on current location and walking the annotations available in the array.
[mv annotations];
Once the closest annotation is calculated, call:
[self openAnnotation:closestAnnotation];
The mapView should scroll automatically to place your annotation in the center of the display area.
In swift 3 this is updated to:
func openAnnotation(annotation: MkAnnotation) {
_ = [mapView .selectAnnotation(annotation, animated: true)]
}
and can be called using any annotation (this will open the annotation callout view and attempt to center the annotation on the map)
For example using the second annotation in a hypothetical list of annotations.
openAnnotation(annotation: mapView.annotations[1])