How would I create an overlay that colors the entire map a certain color? Then I need to be able to place annotations on top of it. Any ideas? Thanks.
What you want is MKOverlay and MKOverlayView.
You can find apple's code in one of the apps mentioned in 'Related sample code'. in above protocol and class reference page.
EDIT : As Per the comments previous code was not working as-is. Heres a MKMapDimOverlay GitHub project which you can simply integrate using CocoaPods. I have also made the relevant changes in following code in the answer.
To explain briefly, following is code for adding a dark overlay on entire map.
You need to create an overlay and add it to the map view.
MKMapDimOverlay *dimOverlay = [[MKMapDimOverlay alloc] initWithMapView:MapView];
[mapView addOverlay: dimOverlay];
Create and return MKOverlayView for the specific MKOverlay in 'viewForOverlay' delegate method
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
if([overlay isMemberOfClass:[MKMapDimOverlay class]]) {
MKMapDimOverlayView *dimOverlayView = [[MKMapDimOverlayView alloc] initWithOverlay:overlay];
return dimOverlayView;
}
}
Since all you want is a colored overlay covering the map, your overlay and overlay view implementation will be very simple.
DimOverlay.m
#interface DimOverlay ()
#property (nonatomic) CLLocationCoordinate2D dimOverlayCoordinates;
#end
#implementation DimOverlay
-(id)initWithMapView:(MKMapView *)mapView {
self = [super init];
if(self)
{
self.dimOverlayCoordinates = mapView.centerCoordinate;
}
return self;
}
-(CLLocationCoordinate2D)coordinate {
return self.dimOverlayCoordinates;
}
-(MKMapRect)boundingMapRect {
return MKMapRectWorld;
}
#end
DimOverlayView.m
#implementation DimOverlayView
- (void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale
inContext:(CGContextRef)ctx {
/*
You can allow custom colors and opacity values.
Simply add UIColor and CGFloat properties in the overlay view class
and use those properties instead of the default hardcodes values below.
*/
CGContextSetAlpha(ctx, 0.85);
CGContextSetFillColorWithColor(ctx, [UIColor blackColor].CGColor);
CGContextFillRect(ctx, [self rectForMapRect:mapRect]);
}
#end
Related
Hi to everyone and thanks in advance =)
I have a doubt related with MKMapView and MKAnnotationView. I need to show annotations with custom images on MKMapView. To do this, and following several tutorials and other stackoverflow answers i created my own class. EDAnnotation.h:
#interface EDAnnotation : MKAnnotationView
//#property (nonatomic, strong) UIImageView *imageView;
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier;
#end
EDAnnotation.m:
#import "EDAnnotation.h"
#implementation EDAnnotation
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self != nil) {
CGRect frame = self.frame;
frame.size = CGSizeMake(15.0, 15.0);
self.frame = frame;
self.backgroundColor = [UIColor clearColor];
self.centerOffset = CGPointMake(-5, -5);
}
return self;
}
-(void) drawRect:(CGRect)rect {
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setAlignment:NSTextAlignmentCenter];
[[UIImage imageNamed:#"train4_transparent.png"] drawInRect:CGRectMake(0, 0, 15, 15)];
}
#end
I've added several of this annotations to my map and everything works as expected. Whenever I tap on an image, a bubble showing some information is showed. The problem is that i need to be able to detect long press gesture over one of this annotations (in addition to the tap gesture to show the bubble). To achieve this, i've tried to add UILongGestureRecognizer to almost everything possible:
The UIImageView commented in the class above.
The 'EDAnnotationView' instance retrieved using (EDAnnotation *) [mapView dequeueReusableAnnotationViewWithIdentifier:identifier]; in viewForAnnotation callback. I've even tried to make this instance draggable and to listen for didChangeDragState calls in order to cancel them as soon as MKAnnotationViewDragStateStarting is triggered, but this didn't work as expected too.
Basically what i need is:
if the user presses over the image specified in drawRect method of EDAnnotation the bubble shows.
if the user long presses over the image specified in drawRect method of EDAnnotation receive a callback that lets me add a new MKPointAnnotation to the map.
Thanks in advance for your help =)
The problem could be also that your gestureRecognizer conflicts with the gestureRecognizers in the mapView. This could happen, because the annotationViews are subviews of the mapView.To solve this problem use the UIGestureRecognizerDelegate. When you initialize your gestureRecognizer, set the delegate property to the class where you implement that protocol, more precisely these two methods:
#pragma mark GestureRecognizerDelegate
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
With easily returning YES in both methods the gestureRecognizer should react. Maybe some other gestureRecognizers from the mapView will now fire their actions too, but unfortunately it's not possible to do the delegation of the mapView's gestureRecognizers.
This workaround helped me, when I was adding a longPressureRecognizer to the mapView. i think it could help you with your issue too.
Did you tried Delegate way of calling annotation?
Create a delegate in Annotation Class
#protocol AnnotationDelegate <NSObject>
#optional
- (void)shouldContinueAnimate;
#end
in implementation file
- (void)shouldContinueAnimate {
//add code for animating
}
Import the delegate where ever required < AnnotationDelegate >
In the image view class you can add both LongPressGestureRecognizer and TapGestureRecognizer for the image.
_longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(handleLongPressGestureRecognizer:)];
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTapGestureRecognizer:)];
[self.imageView addGestureRecognizer:self.longPressGestureRecognizer];
[self.imageView addGestureRecognizer:self.tapGestureRecognizer];
Handle the method:
- (void)handleTapGestureRecognizer:(UIGestureRecognizer *)sender {
if ([self.delegate respondsToSelector:#selector(shouldContinueAnimate)]) {
[self.delegate shouldContinueAnimate];
}
}
- (void)handleLongPressGestureRecognizer:(UIGestureRecognizer *)sender {
if ([self.delegate respondsToSelector:#selector(shouldContinueAnimate)]) {
[self.delegate shouldContinueAnimate];
}
}
Thanks.
i am using google map api for display the location and search bar for search the location but when the GSMMarker display it snippet window hide inside the uisearchbar so any own please help me.
-(void) setupMarkerOnMap:(CLLocation *)loc PlaceName:(NSString *) strCityName
{
[[self getGoogleMap] clear];
[self getNextButton].enabled = YES;
placeMarker = [GMSMarker markerWithPosition:loc.coordinate];
placeMarker.map = [self getGoogleMap];
[placeMarker setTappable:NO];
placeMarker.snippet = strCityName;
placeMarker.icon = [UIImage imageNamed:#"LocationMarker.png"];
GMSCameraUpdate *updateCamera = [GMSCameraUpdate setTarget:placeMarker.position zoom:10.0];
[[self getGoogleMap] animateWithCameraUpdate:updateCamera];
[[self getGoogleMap] setSelectedMarker:placeMarker];
}
these my code snippet for marker add in google map and i attached image which can help you.
anyone can help me.
Thanks
First of all, Make sure that your Google map does not stack together with the search bar. Then you can use delegate to help you move the animate the marker's position inside the Google Map when you tap on any of the marker. Example code:-
Implement the Google Map Delegate
#interface YourViewController ()<GMSMapViewDelegate>
#property (weak, nonatomic) IBOutlet GMSMapView *mapView;
#end
Set the mapView Delegate to the current view controller
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.delegate = self;
}
Whenever a marker is tapped, the
-(BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker{
[mapView animateToLocation:marker.position];
return YES;
}
If you have everything setup correctly and the map is big enough, info window should be displayed nicely inside the map.
In an app I'm working on, users are directed to push a button to drop MKAnnotations onto the map. They will drop 2 or 3 pins, each of which is saved to an #property when the pin is added in didAddAnnotationViews because I need a reference to it later, and I need to know which pin it is - pin 1, 2, or 3 (the order in which they are dropped).
I'm using a custom MKAnnotation and MKAnnotationView class to add a few NSStrings to each annotation, I'm not sure if that's important or not.
I'm creating 3 properties like this:
#property (nonatomic, strong) CustomAnnotationView *ann1;
#property (nonatomic, strong) CustomAnnotationView *ann2;
#property (nonatomic, strong) CustomAnnotationView *ann3;
Here is my didAddAnnotationViews:
- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views
{
for(MKAnnotationView *view in views)
{
if(![view.annotation isKindOfClass:[MKUserLocation class]])
{
CustomAnnotationView *newAnnView = (CustomAnnotationView*)view;
if(newAnnView.type == CustomType1)
{
ann1 = newAnnView;
}
else if(newAnnView.type == CustomType2)
{
ann2 = newAnnView;
}
else if(newAnnView.type == CustomType3)
{
ann3 = newAnnView;
}
}
}
}
Also, here's my viewForAnnotation method:
- (MKAnnotationView *)mapView:(MKMapView *)pMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if([annotation class] == MKUserLocation.class)
{
return nil;
}
CustomAnnotationView *annotationView = [[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"WayPoint"];
annotationView.canShowCallout = YES;
annotationView.draggable = YES;
[annotationView setSelected:YES animated:YES];
[annotationView setRightCalloutAccessoryView:customCalloutButton];
return annotationView;
}
Now, eventually, I need to save the coordinates of these annotations, and here's where things go wrong. Sometimes, but only once in a while, ann1.annotation.coordinate.latitude and ann1.annotation.coordinate.longitude are both 0.0 (this happens with ann1, ann2, or ann3, just using ann1 for example purposes)! Why is this happening? I have a feeling it has something to do with an object reference issue since the MKAnnotationView is still intact, but the annotation is cleared out. Maybe it's bad that I'm assigning the reference with ann1 = newAnnView? Am I supposed to use viewForAnnotation?
Does anyone see something I'm doing wrong?
UPDATE
I looked over my MKAnnotation subclass and I noticed that while I am defining a coordinate property according to the docs, I was not #synthesizing it in my implementation file. I have now added that and I haven't been able to replicate the issue yet...if this ends up being the "fix", I'm still very confused why my code would have worked most of the time without the #synthesize. Maybe I didn't actually fix it, and I'm setting myself up for disappointment later.
I don't think you;re supposed to use didAddAnnotationViews like that. Usually the flow is as follows:
Create an MKAnnotation, or an instance of your subclass of it
Assign the strings you mentioned
Call [mapView addAnnotation:myAnnotation]
In viewForAnnotation create an MKAnnotationView (or CustomAnnotationView( based on the annotation provided as a parameter
When you need to save the coordinates you can either loop through the mapView.annotations array, or if you have kept thre3 variables called ann1, ann2, ann3 could save them one by one.
Of course if you've found a better way or this doesn't fit something else in your app you don't need to use it, but this is the only flow I have seen thus far.
I know this question has been asked by others before and i have read them in this forum previously, but I have tried all the proposed approach without luck so far so I decided to post this question.
I have this piece of code below which is meant to change the pin icon on MKMapView. However the viewForAnnotation function doesn't even seem to get called by the MKMapView. People said that they got some problems with delegating the function to the file's owner which can be done by dragging the map view in .xib file to the file owner or defining myMap.delegate = self. I have done both ways but still get nothing.
Really appreciate for any help for my problem, thanks.
CODE:
- (MKPinAnnotationView*)myMap:(MKMapView*)myMap viewForAnnotation:(id<MKAnnotation>)annotation{
MKPinAnnotationView *pin = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"CustomPin"];
UIImage *icon = [UIImage imageNamed:#"bustour.png"];
UIImageView *iconView = [[UIImageView alloc] initWithFrame:CGRectMake(8,0,32,37)];
if(icon == nil)
NSLog(#"image: ");
else
NSLog(#"image: %#", (NSString*)icon.description);
[iconView setImage:icon];
[pin addSubview:iconView];
pin.canShowCallout = YES;
pin.pinColor = MKPinAnnotationColorPurple;
return pin;
}
DELEGATION
Your delegate method is named incorrectly as myMap:viewForAnnotation:.
The viewForAnnotation delegate method must be named mapView:viewForAnnotation: like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id < MKAnnotation >)annotation
{
//code goes here...
}
The map view will look for that exact method name.
If not found, it won't call it and will create a default red pin instead.
You can change the name of the internal parameters but not the method name.
For example, this would also be ok:
- (MKAnnotationView *)mapView:(MKMapView *)myMap
viewForAnnotation:(id < MKAnnotation >)annotation
{
//code goes here...
}
import bellow class in your .h class..
#import <MapKit/MapKit.h>
and also add MKMapViewDelegate in your this class like bellow...
#interface PTAViewController : UIViewController<MKMapViewDelegate>{
///...... your code..
}
#end
As I understand, you setting outlet, not delegate. To set delegate do like that:
I've looked at several StackOverflow posts and Apple documentation on how to implement overlays in MKMapView. For me, I'm interested specifically in displaying MKPolygon objects on my map. I've found that fundamentally, the process boils down to the following:
Link to MapKit and CoreLocation frameworks
Make an outlet to an MKMapKit object and declare view controller as delegate
Declare a CLLocationCoordinate2D array containing the points of a polygon and create an MKPolygon object with the class method polygonWithCoordinates:count:
Call addOverlay: of map and pass the newly created MKPolygon object as the parameter
Implement (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay
Later on, I'll likely be having to display 20-30 polygons at a given time on the map. However, in my exploration of how to display overlays (hardcoding test examples right now, rather than reading in data from a file), I've found that I can get some overlays to appear, but not others. Reading the Location Awareness Programming Guide by Apple, I came across an example of a polygon overlaid above the state of Colorado. That worked. But when I tried to make a polygon that covered Kansas, I couldn't get it to work. It seems that any polygon that I tried to make on my own (Embry-Riddle Aeronautical University polygon and Kansas polygon) won't display, but those that I got online work perfectly. I used Google Earth to create the polygons and then exported them as KML files to get the coordinates.
Code for the implementation of my ViewController is below. Just trying to find out what I may be unintentionally doing wrong to create this problem. Thanks in advance for help.
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Array of coordinates for polygon covering state of Colorado ... DISPLAYS PERFECTLY
CLLocationCoordinate2D points[4];
points[0] = CLLocationCoordinate2DMake(41.000512, -109.050116);
points[1] = CLLocationCoordinate2DMake(36.99892, -109.045267);
points[2] = CLLocationCoordinate2DMake(36.993076, -102.041981);
points[3] = CLLocationCoordinate2DMake(41.002371, -102.052066);
MKPolygon *polygon = [MKPolygon polygonWithCoordinates:points count:4];
[mapView addOverlay:polygon];
[polygon release];
// Array of coordinates for polygon covering state of Kansas ... DOESN'T DISPLAY
CLLocationCoordinate2D kansasPoints[9];
kansasPoints[0] = CLLocationCoordinate2DMake(-102.0595440241806, 39.99774930940907);
kansasPoints[1] = CLLocationCoordinate2DMake(-102.0424467175215, 36.99846609483674);
kansasPoints[2] = CLLocationCoordinate2DMake(-94.62550551403953, 36.98936020770036);
kansasPoints[3] = CLLocationCoordinate2DMake(-94.58798745384412, 39.11683771419185);
kansasPoints[4] = CLLocationCoordinate2DMake(-94.79955391183, 39.21290793052091);
kansasPoints[5] = CLLocationCoordinate2DMake(-95.13489191971419, 39.51613476830012);
kansasPoints[6] = CLLocationCoordinate2DMake(-94.86553124171813, 39.78380472206268);
kansasPoints[7] = CLLocationCoordinate2DMake(-95.02618283417986, 39.89072859904893);
kansasPoints[8] = CLLocationCoordinate2DMake(-95.31904155494097, 39.99390420513669);
MKPolygon *kansasPolygon = [MKPolygon polygonWithCoordinates:kansasPoints count:9];
[mapView addOverlay:kansasPolygon];
[kansasPolygon release];
// Array of coordinates for polygon covering part of Daytona Beach, FL campus
// of Embry-Riddle Aeronautical University... DOESN'T DISPLAY
CLLocationCoordinate2D erauPoints[7];
erauPoints[0] = CLLocationCoordinate2DMake(-81.05176, 29.18492);
erauPoints[1] = CLLocationCoordinate2DMake(-81.04409, 29.18801);
erauPoints[2] = CLLocationCoordinate2DMake(-81.05166, 29.19293);
erauPoints[3] = CLLocationCoordinate2DMake(-81.05365, 29.19536);
erauPoints[4] = CLLocationCoordinate2DMake(-81.05465, 29.19493);
erauPoints[5] = CLLocationCoordinate2DMake(-81.05376, 29.19323);
erauPoints[6] = CLLocationCoordinate2DMake(-81.05506, 29.19188);
MKPolygon *erauPolygon = [MKPolygon polygonWithCoordinates:erauPoints count:7];
[mapView addOverlay:erauPolygon];
[erauPolygon release];
// Array of coordinates taken from http://www.shawngrimes.me/2011/04/adding-polygon-map-overlays/
// for commuter parking lot at Capitol College in Maryland ... DISPLAYS PERFECTLY
CLLocationCoordinate2D commuterLotCoords[5]={
CLLocationCoordinate2DMake(39.048019,-76.850535),
CLLocationCoordinate2DMake(39.048027,-76.850234),
CLLocationCoordinate2DMake(39.047407,-76.850181),
CLLocationCoordinate2DMake(39.047407,-76.8505),
CLLocationCoordinate2DMake(39.048019,-76.850535)
};
MKPolygon *commuterPoly1 = [MKPolygon polygonWithCoordinates:commuterLotCoords count:5];
[mapView addOverlay:commuterPoly1];
[commuterPoly1 release];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKPolygon class]]) {
MKPolygonView *polygonView = [[[MKPolygonView alloc] initWithOverlay:overlay] autorelease];
polygonView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.3f];
polygonView.strokeColor = [UIColor redColor];
polygonView.lineWidth = 1.0f;
return polygonView;
}
return nil;
}
#end
It looks like the latitude and longitude parameters of the coordinates for the polygons that don't display are backwards.
For example, this:
kansasPoints[0] = CLLocationCoordinate2DMake(-102.0595440241806, 39.99774930940907);
should be
kansasPoints[0] = CLLocationCoordinate2DMake(39.99774930940907, -102.0595440241806);
Also, you should not be calling release on the MKPolygon objects you are creating using polygonWithCoordinates since they will be autoreleased.