Add inverted circle overlay to map view

前端 未结 4 1482
天命终不由人
天命终不由人 2020-12-14 11:31

(Using iOS 5 and Xcode 4.2.)

I\'ve followed the instructions here: http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/LocationA

4条回答
  •  一向
    一向 (楼主)
    2020-12-14 11:58

    I had the same task and here is how I solve it:

    NOTE: this code will only work starting from iOS7

    Add an overlay to the map, somewhere in your view controller:

    MyMapOverlay *overlay = [[MyMapOverlay alloc] initWithCoordinate:coordinate];
    [self.mapView addOverlay:overlay level:MKOverlayLevelAboveLabels];
    

    In the MKMapViewDelegate methods write next:

    - (MKOverlayRenderer *)mapView:(MKMapView *)map rendererForOverlay:(id)overlay {
        /// we need to draw overlay on the map in a way when everything except the area in radius of 500 should be grayed
        /// to do that there is special renderer implemented - NearbyMapOverlay
        if ([overlay isKindOfClass:[NearbyMapOverlay class]]) {
            MyMapOverlayRenderer *renderer = [[MyMapOverlayRenderer alloc] initWithOverlay:overlay];
            renderer.fillColor = [UIColor whateverColor];/// specify color which you want to use for gray out everything out of radius
            renderer.diameterInMeters = 1000;/// choose whatever diameter you need
    
            return renderer;
        }
        return nil;
    }
    

    The MyMapOverlay itself should be something like followed:

    @interface MyMapOverlay : NSObject
    - (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate;
    @end
    
    @implementation MyMapOverlay
    
    @synthesize coordinate = _coordinate;
    
    - (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate {
        self = [super init];
        if (self) {
            _coordinate = coordinate;
        }
        return self;
    }
    
    - (MKMapRect)boundingMapRect {
        return MKMapRectWorld;
    }
    
    @end
    

    And the MyMapOverlayRenderer:

    @interface MyMapOverlayRenderer : MKOverlayRenderer
    @property (nonatomic, assign) double diameterInMeters;
    @property (nonatomic, copy) UIColor *fillColor;
    @end
    
    @implementation MyMapOverlayRenderer
    
    /// this method is called as a part of rendering the map, and it draws the overlay polygon by polygon
    /// which means that it renders overlay by square pieces
    - (void)drawMapRect:(MKMapRect)mapRect
          zoomScale:(MKZoomScale)zoomScale
          inContext:(CGContextRef)context {
    
        /// main path - whole area
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(mapRect.origin.x, mapRect.origin.y, mapRect.size.width, mapRect.size.height)];
    
        /// converting to the 'world' coordinates
        double radiusInMapPoints = self.diameterInMeters * MKMapPointsPerMeterAtLatitude(self.overlay.coordinate.latitude);
        MKMapSize radiusSquared = {radiusInMapPoints, radiusInMapPoints};
        MKMapPoint regionOrigin = MKMapPointForCoordinate(self.overlay.coordinate);
        MKMapRect regionRect = (MKMapRect){regionOrigin, radiusSquared}; //origin is the top-left corner
        regionRect = MKMapRectOffset(regionRect, -radiusInMapPoints/2, -radiusInMapPoints/2);
        // clamp the rect to be within the world
        regionRect = MKMapRectIntersection(regionRect, MKMapRectWorld);
    
        /// next path is used for excluding the area within the specific radius from current user location, so it will not be filled by overlay fill color
        UIBezierPath *excludePath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(regionRect.origin.x, regionRect.origin.y, regionRect.size.width, regionRect.size.height) cornerRadius:regionRect.size.width / 2];
        [path appendPath:excludePath];
    
        /// setting overlay fill color
        CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
        /// adding main path. NOTE that exclusionPath was appended to main path, so we should only add 'path'
        CGContextAddPath(context, path.CGPath);
        /// tells the context to fill the path but with regards to even odd rule
        CGContextEOFillPath(context);
    }
    

    As a result you will have exact same view like on the left image that was posted in the question.

提交回复
热议问题