Как нарисовать MKCircle, цвет заливки которого не сплошной, а изображение

Я узнал, как рисовать крикет вокруг аннотации карты. Я делаю это так:

     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;
}

Он работает нормально, но я хотел бы нарисовать круг, цвет заливки которого не будет таким:

enter image description here

Ответ 1

Чтобы нарисовать круг с градиентом, вы должны предоставить собственный класс представления аннотаций, поскольку ни один из существующих не поддерживает это. Что вы можете сделать, так это то, что вы можете переопределить метод - (void)fillPath:(CGPathRef)path inContext:(CGContextRef)context в подклассе MKCircleView. Ниже приведен код (не оптимизированный, с жестко заданными параметрами заполнения), чтобы начать работу:

@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);
}

Чтобы использовать его, просто замените MKCircleView на TWOGradientCircleView:

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

    return circleView;
}

Если вы хотите использовать изображение вместо рисования градиента, вы можете заменить рисунок градиента выше графическим рисунком. Поскольку масштабирование затем размывает изображение, вы должны либо отключить масштабирование, либо нарисовать изображение, как продемонстрировано Apple в сеансе на WWDC10 (см. здесь для репозиторий со своим примером кода). Установка UIColor с изображением шаблона не работает радиусом 15000 (если вы не используете действительно, действительно огромное изображение;)).

Ответ 2

Ответ для iOS 7 с помощью MKCircleRenderer...

Вы должны подклассифицировать MKCircleRenderer и переопределить метод fillPath:inContext, как и принятый ответ на этот вопрос. например.

@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);


}

И затем в вашем делете MKMapView выполните следующий метод...

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

    MKCircle * circle = (MKCircle *)overlay;

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

    return renderer;

}

Это позволит вам добиться такого же эффекта, но использовать новые методы, доступные в iOS 7.

Ответ 3

Вы пытались установить цвет заливки на цвет, созданный с помощью [UIColor colorWithPatternImage:]?

Ответ 4

Чтобы реализовать решение в Swift 2.0 (iOS7 +), я использовал следующее решение

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);
    }
}

И в вашем MKMapViewDelegate вам нужно будет добавить

func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay is MKCircle {
            let circleRenderer = TWOGradientCircleRenderer(overlay: overlay)
            return circleRenderer
        } else {
            return MKOverlayRenderer()
        }
    }

Ответ 5

Этот ответ предназначен для Swift 3.0/iOS 10.

class CircleRenderer: MKCircleRenderer {
    override func fillPath(_ path: CGPath, in context: CGContext) {
        let rect: CGRect = path.boundingBox
        context.addPath(path)
        context.clip()
        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()
        guard let gradient = CGGradient(colorSpace: colorSpace, colorComponents: gradientColors, locations: gradientLocations, count: 2) else { return }

        let gradientCenter = CGPoint(x: rect.midX, y: rect.midY)
        let gradientRadius = min(rect.size.width, rect.size.height) / 2
        context.drawRadialGradient(gradient, startCenter: gradientCenter, startRadius: 0, endCenter: gradientCenter, endRadius: gradientRadius, options: .drawsAfterEndLocation)
    }
}

И метод делегирования наложения:

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    let circleRenderer = CircleRenderer(overlay: overlay)

    return circleRenderer
}

Пример этого на работе:

введите описание изображения здесь