Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw a MKCircle whose fill color is not solid but an image

I found out how to draw a cricle around map annotation. I do it like this:

     MKCircle *circle = [MKCircle circleWithCenterCoordinate:theCoordinate radius:15000];
     [myMap addOverlay:circle];

 -(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay
 {
    MKCircleView *circleView = [[MKCircleView alloc] initWithOverlay:overlay];
    circleView.fillColor =[UIColor redColor];

   return circleView;
}

It works ok but i would like to draw a circle whose fill color is not solid like this:

enter image description here

like image 220
DixieFlatline Avatar asked Jan 28 '13 16:01

DixieFlatline


4 Answers

Answer for iOS 7 using MKCircleRenderer...

You should subclass MKCircleRenderer, and override the fillPath:inContext method, similarly to the accepted answer to this question. e.g.

@implementation MKGradientCircleRenderer

- (void)fillPath:(CGPathRef)path inContext:(CGContextRef)context
{

    CGRect rect = CGPathGetBoundingBox(path);

    CGContextAddPath(context, path);
    CGContextClip(context);

    CGFloat gradientLocations[2] = {0.6f, 1.0f};
    // Start color white with 0.25 alpha,
    // End color green with 0.25 alpha
    CGFloat gradientColors[8] = {1.0f, 1.0f, 1.0f, 0.25f, 0.0f, 1.0f, 0.0f, 0.25f};
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocations, 2);
    CGColorSpaceRelease(colorSpace);

    CGPoint gradientCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    CGFloat gradientRadius = MIN(rect.size.width, rect.size.height) / 2;

    CGContextDrawRadialGradient(context, gradient, gradientCenter, 0, gradientCenter, gradientRadius, kCGGradientDrawsAfterEndLocation);

    CGGradientRelease(gradient);


}

And then in your MKMapView delegate, implement the following method...

-(MKOverlayRenderer *)mapView:(MKMapView*)mapView rendererForOverlay:(id<MKOverlay>)overlay {

    MKCircle * circle = (MKCircle *)overlay;

    MKGradientCircleRenderer * renderer = [[MKGradientCircleRenderer alloc] initWithCircle:circle];

    return renderer;

}

This will allow you to achieve the same effect, but using the new methods available in iOS 7.

like image 85
Sammio2 Avatar answered Nov 02 '22 23:11

Sammio2


To draw a circle with a gradient, you have to provide an own annotation view class, as none of the existing ones support that. What you can do is you can override the method - (void)fillPath:(CGPathRef)path inContext:(CGContextRef)context in a subclass of MKCircleView. Here is some code (non-optimized, with hardcoded fill parameters) to get you started:

@interface TWOGradientCircleView : MKCircleView
@end

@implementation TWOGradientCircleView

- (void)fillPath:(CGPathRef)path inContext:(CGContextRef)context
{
    CGRect rect = CGPathGetBoundingBox(path);

    CGContextAddPath(context, path);
    CGContextClip(context);

    CGFloat gradientLocations[2] = {0.6f, 1.0f};
    // Start color white with 0.25 alpha,
    // End color green with 0.25 alpha
    CGFloat gradientColors[8] = {1.0f, 1.0f, 1.0f, 0.25f, 0.0f, 1.0f, 0.0f, 0.25f};
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocations, 2);
    CGColorSpaceRelease(colorSpace);

    CGPoint gradientCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    CGFloat gradientRadius = MIN(rect.size.width, rect.size.height) / 2;

    CGContextDrawRadialGradient(context, gradient, gradientCenter, 0, gradientCenter, gradientRadius, kCGGradientDrawsAfterEndLocation);

    CGGradientRelease(gradient);
}

To use it, just replace MKCircleView with TWOGradientCircleView:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay
{
    MKCircleView *circleView = [[TWOGradientCircleView alloc] initWithOverlay:overlay];

    return circleView;
}

If you would like to use an image instead of drawing the gradient, you can replace the gradient drawing above with image drawing. As zooming in would then blur the image, you should either disable zoom, or tile the image as Apple demonstrated in a session at WWDC10 (see here for a repository with their sample code). Setting a UIColor with a pattern image does not work for a radius of 15000 (unless you use a really, really huge image ;)).

like image 36
Tammo Freese Avatar answered Nov 03 '22 01:11

Tammo Freese


To implement the solution in Swift 2.0 (iOS7+) I used the following solution

import Foundation
import MapKit

class TWOGradientCircleRenderer: MKCircleRenderer {

    override func fillPath(path: CGPath, inContext context: CGContext) {
        let rect:CGRect = CGPathGetBoundingBox(path)

        CGContextAddPath(context, path);
        CGContextClip(context);

        let gradientLocations: [CGFloat]  = [0.6, 1.0];
        let gradientColors: [CGFloat] = [1.0, 1.0, 1.0, 0.25, 0.0, 1.0, 0.0, 0.25];
        let colorSpace = CGColorSpaceCreateDeviceRGB();
        let gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocations, 2);

        let gradientCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
        let gradientRadius = min(rect.size.width, rect.size.height) / 2;

        CGContextDrawRadialGradient(context, gradient, gradientCenter, 0, gradientCenter, gradientRadius, .DrawsAfterEndLocation);
    }
}

And in your MKMapViewDelegate you will need to add

func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay is MKCircle {
            let circleRenderer = TWOGradientCircleRenderer(overlay: overlay)
            return circleRenderer
        } else {
            return MKOverlayRenderer()
        }
    }
like image 39
maninvan Avatar answered Nov 02 '22 23:11

maninvan


Have you tried setting the fill colour to a colour created with [UIColor colorWithPatternImage:]?

like image 43
Jonathan Grynspan Avatar answered Nov 02 '22 23:11

Jonathan Grynspan