Domanda

I've created a MKMapView with MKPolygons based on coordinates. There are multiple polygons on the map (look here for an example of what I am re-creating as an app).

What I am trying to do is when the user touches the polygon, it opens a popover view with information about the location. This information is currently stored inside a plist file with the coordinates.

What I currently have so far is that I am able to get touch event and print to the log that the polygon was touched.

The question that I have is:
Can MKPolygonView be used like an MKAnnotationView where once the user taps the pin more information pops up about that current location?

I want to do the same for the polygon view. When touched, the user would see more information about the location that is stored in the plist. If it is possible what would be the best way to get it to work?

My current code is below.

#import "outagemapViewController.h"
#import "MyAnnotation.h"
#import "WildcardGestureRecognizer.h"
#define METERS_PER_MILE 46309.344
@interface outagemapViewController ()

@end

@implementation outagemapViewController
- (void)viewDidLoad {

outages = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"outages"ofType:@"plist"]];


for (NSDictionary *coloredAreas in outages) {
   coordinateData = coloredAreas[@"coords"];
test = coloredAreas[@"outages"];

    NSLog(@"test %@", test);
    coordsLen = [coordinateData count];
      NSLog(@"coords %d", coordsLen);
    CLLocationCoordinate2D coords[coordsLen];
    for (i=0; i < coordsLen; i++) {
        NSString *lat = coordinateData[i];
        NSArray *latt = [lat componentsSeparatedByString:@","];
        double latitude = [[latt objectAtIndex:0] doubleValue];
        double longitude = [[latt objectAtIndex:1] doubleValue];
       coords[i] = CLLocationCoordinate2DMake(latitude, longitude);

    }


 MKPolygon* poly2 = [MKPolygon polygonWithCoordinates:coords count:coordsLen];
 poly2.title=@"test";
 [self.mapView addOverlay:poly2];
 }
 }
 - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
{
    if ([overlay isKindOfClass:[MKPolygon class]])
    {

        MKPolygonView*    aView = [[MKPolygonView alloc] initWithPolygon:(MKPolygon*)overlay];

        int numbers = [test intValue];

        if(numbers >= 10){
            aView.fillColor = [[UIColor greenColor] colorWithAlphaComponent:0.6];
            aView.strokeColor = [[UIColor greenColor] colorWithAlphaComponent:1.0];
            aView.lineWidth = 3;
        }else if(numbers < 10){
            aView.fillColor = [[UIColor yellowColor] colorWithAlphaComponent:0.6];
            aView.strokeColor = [[UIColor yellowColor] colorWithAlphaComponent:1.0];
            aView.lineWidth = 3;

                    }

        return aView;
    }


    return nil;
}
 }

-(void)viewWillAppear:(BOOL)animated{
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = 35.20418;
zoomLocation.longitude = -89.86862;

MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation,         0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);

[_mapView setRegion:viewRegion animated:YES];

WildcardGestureRecognizer * tapInterceptor = [[WildcardGestureRecognizer alloc] init];
tapInterceptor.touchesBeganCallback = ^(NSSet * touches, UIEvent * event) {
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self.mapView];

    CLLocationCoordinate2D coord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
    MKMapPoint mapPoint = MKMapPointForCoordinate(coord);
    for (id overlay in self.mapView.overlays)
    {
        if ([overlay isKindOfClass:[MKPolygon class]])
        {
            MKPolygon *poly = (MKPolygon*) overlay;
            id view = [self.mapView viewForOverlay:poly];
            if ([view isKindOfClass:[MKPolygonView class]])
            {
                MKPolygonView *polyView = (MKPolygonView*) view;
                CGPoint polygonViewPoint = [polyView pointForMapPoint:mapPoint];
                BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polyView.path, NULL, polygonViewPoint, NO);
                if (mapCoordinateIsInPolygon) {
                   // debug(@"hit!");
                    NSLog(@"hit");
                } else {
                     NSLog(@"miss");
                }
            }
        }
    }

};
[self.mapView addGestureRecognizer:tapInterceptor];
}

- (void)didReceiveMemoryWarning
{
 [super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end
È stato utile?

Soluzione

Unfortunately, for overlays, there's no built-in touch-detection and callout view like there is for annotations.

You'll have to do the touch-detection manually like you're already doing (and it looks like it should work).
(Even more unfortunate here is that adding a gesture recognizer directly to the overlay view doesn't work -- you have to add it to the whole map and then check whether the touch point is in any overlay.)

For an overlay callout view, once you've detected a touch on an overlay, you can create a custom UIView and do addSubview. I suggest adding it to the map instead of the overlay view and you might be able to use the CGPoint point you are already calculating to determine the frame of the custom callout view.

You might also want to keep a ivar/property reference to the overlay callout view so it can be easily removed and re-added if the user taps on another overlay while the callout for another overlay is already displayed.

Another option which is probably easier is to create a custom UIViewController and present or push it. The specifics of showing it depend on whether you're using a navigation controller and/or storyboard.

If your app is also built for iPad, you could also show the "callout" using a UIPopoverController.
See How do I display a UIPopoverView as a annotation to the map view? (iPad) for a code example (it's with an annotation but you should be able to adapt it for the overlay).


Once you've identified which overlay was tapped, you need to display its associated data which is in your original data source (the outages array). Right now, overlays are created and added but have no reference back to the original data object (outage dictionary in outages array).

(Subclassing MKPolygon to add a custom property has issues and workarounds and creating a completely custom MKOverlay class introduces a lot of other additional work.)

For your current data source structure, a simple, quick (and somewhat crude) option is to set the overlay's title property to the index in the outages array of the outage object associated with the overlay. Since the title property is an NSString and the array index is an integer, we'll convert it to a string:

NSUInteger outageIndex = [outages indexOfObject:coloredAreas];
poly2.title = [NSString stringWithFormat:@"%d", outageIndex];
[self.mapView addOverlay:poly2];

In viewForOverlay, it looks like you're using test (which comes from an outage object) to determine the polygon's color. The value of the externally declared/set test variable will not necessarily be in sync with the overlay the delegate method is currently being called for (the map could call viewForOverlay multiple times for the same overlay and not necessarily in the order you add them). You have to retrieve the outage object based on some property of the overlay parameter. Since we are setting the overlay's title property to the outage's index:

//int numbers = [test intValue];  <-- remove this line

int outageIndex = [overlay.title intValue];
NSDictionary *outageDict = [outages objectAtIndex:outageIndex];
id outageNumbersObject = outageDict[@"outages"]; 
//replace id above with actual type
//can't tell from code in question whether it's NSString or NSNumber 
int numbers = [outageNumbersObject intValue];

//use "numbers" to set polygon color...

Finally, when an overlay is tapped, you use the same method as in viewForOverlay to get the outage object:

if (mapCoordinateIsInPolygon) {
    int outageIndex = [overlay.title intValue];
    NSDictionary *outageDict = [outages objectAtIndex:outageIndex];
    NSLog(@"hit, outageDict = %@", outageDict);
    //show view with info from outageDict...
} 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top