Подтвердить что ты не робот

Рисовать внешний полукруг с градиентом, используя основную графику в iOS?

Я хочу нарисовать фигуру, как на прикрепленном изображении, используя основную графику в iOS. Это возможно. Если возможно, укажите пример кода. enter image description here

Я хочу по крайней мере 3 градиента цвета по форме.

4b9b3361

Ответ 1

Если вы хотите нарисовать это в CGContext:

Используйте +[UIBezierPath bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:] для создания пути, содержащего полукруглую дугу. Затем используйте CGPathCreateCopyByStrokingPath, чтобы создать замкнутую форму вокруг дуги. Используйте CGContextAddPath, чтобы добавить эту закрытую форму в свой контекстный путь, затем используйте CGContextClip, чтобы установить область клипа в толстую дугу.

Используйте CGGradientCreateWithColors для создания CGGradient с цветами радуги. Используйте CGContextDrawLinearGradient, чтобы заполнить область клипа градиентом.

Если вы хотите нарисовать это, используя CALayer:

Создайте CAGradientLayer. Установите свойство layer colors в свою радугу цветов. Установите слой startPoint в (0,0) и слой endPoint в (1,0).

Создайте CAShapeLayer и установите его как маску слоя градиента. Используйте +[UIBezierPath bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:] и CGPathCreateCopyByStrokingPath, чтобы создать путь, охватывающий толстую дугу, и установите этот путь как слой формы path.

Ответ 2

Градиенты естественно не прокладывают путь. Вы должны имитировать это. Код использует NSBezierPath, NSView и CGContext, но его не следует переносить в iOS.

Я сделал с сборкой трапеций.

Вот пример drawRect из NSView, который рисует градиенты:

@implementation grad
-(void)drawRect:(NSRect)dirtyRect {

    [[NSColor whiteColor]set];
    NSRectFill([self bounds]);
    float dim = MIN(self.bounds.size.width, self.bounds.size.height);
    int subdiv=512;
    float r=dim/4;
    float R=dim/2;

    float halfinteriorPerim = M_PI*r;
    float halfexteriorPerim = M_PI*R;
    float smallBase= halfinteriorPerim/subdiv;
    float largeBase= halfexteriorPerim/subdiv;

    NSBezierPath * cell = [NSBezierPath bezierPath];

    [cell moveToPoint:NSMakePoint(- smallBase/2, r)];
    [cell lineToPoint:NSMakePoint(+ smallBase/2, r)];

    [cell lineToPoint:NSMakePoint( largeBase /2 , R)];
    [cell lineToPoint:NSMakePoint(-largeBase /2,  R)];
    [cell closePath];

    float incr = M_PI / subdiv;
    CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort];
    CGContextTranslateCTM(ctx, +self.bounds.size.width/2, +self.bounds.size.height/2);

    CGContextScaleCTM(ctx, 0.9, 0.9);
    CGContextRotateCTM(ctx, M_PI/2);
    CGContextRotateCTM(ctx,-incr/2);

    for (int i=0;i<subdiv;i++) {
        // replace this color with a color extracted from your gradient object
        [[NSColor colorWithCalibratedHue:(float)i/subdiv saturation:1 brightness:1 alpha:1] set];
        [cell fill];
        [cell stroke];
        CGContextRotateCTM(ctx, -incr);
    }
}

Это выглядит так: с различными комбинациями subdiv и r (внутренний радиус) и в разных масштабах.

Gradient along a circle

Новая версия с блоками, iOS ready

В этой версии используются Objective-C блоки для цветовых и контурных функций. Просто передайте в блок функцию, возвращающую внутренний радиус, внешний радиус и цвет для любого числа от 0 до 1. Другими параметрами являются начальный угол, конечный угол, количество подразделений, центр и масштаб для цели отладки, а также CGContextRef.

#import "GradientView.h"

@implementation GradientView

typedef void (^voidBlock)(void);
typedef float (^floatfloatBlock)(float);
typedef UIColor * (^floatColorBlock)(float);

-(CGPoint) pointForTrapezoidWithAngle:(float)a andRadius:(float)r  forCenter:(CGPoint)p{
    return CGPointMake(p.x + r*cos(a), p.y + r*sin(a));
}

-(void)drawGradientInContext:(CGContextRef)ctx  startingAngle:(float)a endingAngle:(float)b intRadius:(floatfloatBlock)intRadiusBlock outRadius:(floatfloatBlock)outRadiusBlock withGradientBlock:(floatColorBlock)colorBlock withSubdiv:(int)subdivCount withCenter:(CGPoint)center withScale:(float)scale
{
    float angleDelta = (b-a)/subdivCount;
    float fractionDelta = 1.0/subdivCount;

    CGPoint p0,p1,p2,p3, p4,p5;
    float currentAngle=a;
    p4=p0 = [self pointForTrapezoidWithAngle:currentAngle andRadius:intRadiusBlock(0) forCenter:center];
    p5=p3 = [self pointForTrapezoidWithAngle:currentAngle andRadius:outRadiusBlock(0) forCenter:center];
    CGMutablePathRef innerEnveloppe=CGPathCreateMutable(),
    outerEnveloppe=CGPathCreateMutable();

    CGPathMoveToPoint(outerEnveloppe, 0, p3.x, p3.y);
    CGPathMoveToPoint(innerEnveloppe, 0, p0.x, p0.y);
    CGContextSaveGState(ctx);

    CGContextSetLineWidth(ctx, 1);

    for (int i=0;i<subdivCount;i++)
    {
        float fraction = (float)i/subdivCount;
        currentAngle=a+fraction*(b-a);
        CGMutablePathRef trapezoid = CGPathCreateMutable();

        p1 = [self pointForTrapezoidWithAngle:currentAngle+angleDelta andRadius:intRadiusBlock(fraction+fractionDelta) forCenter:center];
        p2 = [self pointForTrapezoidWithAngle:currentAngle+angleDelta andRadius:outRadiusBlock(fraction+fractionDelta) forCenter:center];

        CGPathMoveToPoint(trapezoid, 0, p0.x, p0.y);
        CGPathAddLineToPoint(trapezoid, 0, p1.x, p1.y);
        CGPathAddLineToPoint(trapezoid, 0, p2.x, p2.y);
        CGPathAddLineToPoint(trapezoid, 0, p3.x, p3.y);
        CGPathCloseSubpath(trapezoid);

        CGPoint centerofTrapezoid = CGPointMake((p0.x+p1.x+p2.x+p3.x)/4, (p0.y+p1.y+p2.y+p3.y)/4);

        CGAffineTransform t = CGAffineTransformMakeTranslation(-centerofTrapezoid.x, -centerofTrapezoid.y);
        CGAffineTransform s = CGAffineTransformMakeScale(scale, scale);
        CGAffineTransform concat = CGAffineTransformConcat(t, CGAffineTransformConcat(s, CGAffineTransformInvert(t)));
        CGPathRef scaledPath = CGPathCreateCopyByTransformingPath(trapezoid, &concat);

        CGContextAddPath(ctx, scaledPath);
        CGContextSetFillColorWithColor(ctx,colorBlock(fraction).CGColor);
        CGContextSetStrokeColorWithColor(ctx, colorBlock(fraction).CGColor);
        CGContextSetMiterLimit(ctx, 0);

        CGContextDrawPath(ctx, kCGPathFillStroke);

        CGPathRelease(trapezoid);
        p0=p1;
        p3=p2;

        CGPathAddLineToPoint(outerEnveloppe, 0, p3.x, p3.y);
        CGPathAddLineToPoint(innerEnveloppe, 0, p0.x, p0.y);
    }
    CGContextSetLineWidth(ctx, 10);
    CGContextSetLineJoin(ctx, kCGLineJoinRound);
    CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
    CGContextAddPath(ctx, outerEnveloppe);
    CGContextAddPath(ctx, innerEnveloppe);
    CGContextMoveToPoint(ctx, p0.x, p0.y);
    CGContextAddLineToPoint(ctx, p3.x, p3.y);
    CGContextMoveToPoint(ctx, p4.x, p4.y);
    CGContextAddLineToPoint(ctx, p5.x, p5.y);
    CGContextStrokePath(ctx);
}

-(void)drawRect:(CGRect)rect {

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    [[UIColor whiteColor] set];
    UIRectFill(self.bounds);

    CGRect r = self.bounds;

    r=CGRectInset(r, 60, 60);

    if (r.size.width > r.size.height)
        r.size.width=r.size.height;
    else r.size.height=r.size.width;

    float radius=r.size.width/2;

    [self drawGradientInContext:ctx  startingAngle:M_PI/16 endingAngle:2*M_PI-M_PI/16 intRadius:^float(float f) {
//        return 0*f + radius/2*(1-f);
        return 200+10*sin(M_PI*2*f*7);
//        return 50+sqrtf(f)*200;
//        return radius/2;
    } outRadius:^float(float f) {
//         return radius *f + radius/2*(1-f);
        return radius;
//        return 300+10*sin(M_PI*2*f*17);
    } withGradientBlock:^UIColor *(float f) {

//        return [UIColor colorWithHue:f saturation:1 brightness:1 alpha:1];
        float sr=90, sg=54, sb=255;
        float er=218, eg=0, eb=255;
        return [UIColor colorWithRed:(f*sr+(1-f)*er)/255. green:(f*sg+(1-f)*eg)/255. blue:(f*sb+(1-f)*eb)/255. alpha:1];

    } withSubdiv:256 withCenter:CGPointMake(CGRectGetMidX(r), CGRectGetMidY(r)) withScale:1];

}

@end

Примеры:

Curved gradient 1

Curved gradient 2

Ответ 3

Так как iOS 8, самый простой и быстрый способ рисовать круговые градиенты - это использование фильтров Core Image. Ядро связано с комментариями к этому ответу, но поскольку выяснение того, как на самом деле это использовать, потребовалось некоторое время, я выведу весь процесс здесь.

По сути, фильтры Core Image представляют собой небольшие фрагментарные шейдеры, которые запускаются на каждом пикселе входного изображения. Хотя они были предназначены для фильтрации изображений, вы можете использовать их для генерации изображений, просто игнорируя значение входного пикселя в ядре фильтра.

В приведенном ниже коде создается фиктивное изображение для использования в качестве входных данных, затем применяется к нему специальное ядро ​​CI для создания кругового градиента.

// 1 - generate a dummy image of the required size
UIGraphicsBeginImageContextWithOptions(CGSizeMake(512.0, 256.0), NO, [[UIScreen mainScreen] scale]);
CIImage *dummyImage = [CIImage imageWithCGImage:UIGraphicsGetImageFromCurrentImageContext().CGImage];

// 2 - define the kernel algorithm
NSString *kernelString = @"kernel vec4 circularGradientKernel(__color startColor, __color endColor, vec2 center, float innerR, float outerR) { \n"
"    vec2 point = destCoord() - center;"
"    float rsq = point.x * point.x + point.y * point.y;"
"    float theta = mod(atan(point.y, point.x), radians(360.0));"
"    return (rsq > innerR*innerR && rsq < outerR*outerR) ? mix(startColor, endColor, theta/radians(360.0)*2.0) : vec4(0.0, 0.0, 0.0, 1.0);"
"}";

// 3 - initialize a Core Image context and the filter kernel
CIContext *context = [CIContext contextWithOptions:nil];
CIColorKernel *kernel = [CIColorKernel kernelWithString:kernelString];

// 4 - argument array, corresponding to the first line of the kernel string
NSArray *args = @[ [CIColor colorWithRed:1.0 green:0.0 blue:0.0],
                   [CIColor colorWithRed:0.0 green:0.0 blue:1.0],
                   [CIVector vectorWithCGPoint:CGPointMake(CGRectGetMidX(dummyImage.extent),CGRectGetMinY(dummyImage.extent))],
                   [NSNumber numberWithFloat:250.0],
                   [NSNumber numberWithFloat:500.0]];

// 5 - apply the kernel to our dummy image, and convert the result to a UIImage
CIImage *ciOutputImage = [kernel applyWithExtent:dummyImage.extent arguments:args];
CGImageRef cgOutput = [context createCGImage:ciOutputImage fromRect:ciOutputImage.extent];
UIImage *gradientImage = [UIImage imageWithCGImage:cgOutput];
CGImageRelease(cgOutput);

Результат выглядит следующим образом:

Круговой градиент, сгенерированный с использованием фильтра Core Image.

Цвета и центральная точка передаются в виде CIColor и CIVector s, а радиусы внутреннего и внешнего окружностей задаются как поплавки. Вы можете, конечно, изменить список аргументов, если хотите передать другие параметры.

Этот метод достаточно быстр, чтобы использовать его для анимации, хотя, если вы хотите сделать это, убедитесь, что используют один и тот же контекст для каждого фрейма, поскольку создание и разрывание его - это узкое место в производительности. Просто убедитесь, что ваш параметр анимации является входным аргументом и петлями над шагами 4 и 5.

Кроме того, не забудьте позвонить в CGImageRelease в конце или у вас будет утечка памяти.

Ответ 4

Я использовал Antoine и настроил его для iOS:

-(void)drawRect:(CGRect)rect {
    // http://stackoverflow.com/a/12775798/129202
    CGContextRef context = UIGraphicsGetCurrentContext();
    [[UIColor whiteColor] set]; //[[NSColor whiteColor]set];
    UIRectFill([self bounds]);
    float dim = MIN(self.bounds.size.width, self.bounds.size.height);
    int subdiv=512;
    float r=dim/4;
    float R=dim/2;

    float halfinteriorPerim = M_PI*r;
    float halfexteriorPerim = M_PI*R;
    float smallBase= halfinteriorPerim/subdiv;
    float largeBase= halfexteriorPerim/subdiv;

    UIBezierPath * cell = [UIBezierPath bezierPath];

    [cell moveToPoint:CGPointMake(- smallBase/2, r)];

    [cell addLineToPoint:CGPointMake(+ smallBase/2, r)];

    [cell addLineToPoint:CGPointMake( largeBase /2 , R)];
    [cell addLineToPoint:CGPointMake(-largeBase /2,  R)];
    [cell closePath];

    float incr = M_PI / subdiv;
    //CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort];
    CGContextTranslateCTM(context, +self.bounds.size.width/2, +self.bounds.size.height/2);

    CGContextScaleCTM(context, 0.9, 0.9);
    CGContextRotateCTM(context, M_PI/2);
    CGContextRotateCTM(context,-incr/2);

    for (int i=0;i<subdiv;i++) {
        // replace this color with a color extracted from your gradient object
        [[UIColor colorWithHue:(float)i/subdiv saturation:1 brightness:1 alpha:1] set];
        [cell fill];
        [cell stroke];
        CGContextRotateCTM(context, -incr);
    }
}

Ответ 5

В качестве другого ответа я нашел это: https://github.com/paiv/AngleGradientLayer

Я попробовал, и он работает хорошо. Я не изучал реализацию. Я не думаю, что мне нужно.: -)

Код на данный момент:

//
// The MIT License (MIT)
// 
// Copyright (C) 2012 Pavel Ivashkov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//
//  AngleGradientLayer.m
//  paiv
//
//  Created by Pavel Ivashkov on 2012-02-12.
//

#import "AngleGradientLayer.h"

#if __has_feature(objc_arc)
#define BRIDGE_CAST(T) (__bridge T)
#else
#define BRIDGE_CAST(T) (T)
#endif

#define byte unsigned char
#define F2CC(x) ((byte)(255 * x))
#define RGBAF(r,g,b,a) (F2CC(r) << 24 | F2CC(g) << 16 | F2CC(b) << 8 | F2CC(a))
#define RGBA(r,g,b,a) ((byte)r << 24 | (byte)g << 16 | (byte)b << 8 | (byte)a)
#define RGBA_R(c) ((uint)c >> 24 & 255)
#define RGBA_G(c) ((uint)c >> 16 & 255)
#define RGBA_B(c) ((uint)c >> 8 & 255)
#define RGBA_A(c) ((uint)c >> 0 & 255)

@interface AngleGradientLayer()

- (CGImageRef)newImageGradientInRect:(CGRect)rect;

@end


static void angleGradient(byte* data, int w, int h, int* colors, int colorCount, float* locations, int locationCount);


@implementation AngleGradientLayer

- (id)init
{
    if (!(self = [super init]))
        return nil;

    self.needsDisplayOnBoundsChange = YES;

    return self;
}

#if !__has_feature(objc_arc)
- (void)dealloc
{
    [_colors release];
    [_locations release];
    [super dealloc];
}
#endif

- (void)drawInContext:(CGContextRef)ctx
{
    CGContextSetFillColorWithColor(ctx, self.backgroundColor);
    CGRect rect = CGContextGetClipBoundingBox(ctx);
    CGContextFillRect(ctx, rect);

    CGImageRef img = [self newImageGradientInRect:rect];
    CGContextDrawImage(ctx, rect, img);
    CGImageRelease(img);
}

- (CGImageRef)newImageGradientInRect:(CGRect)rect
{
    return [[self class] newImageGradientInRect:rect colors:self.colors locations:self.locations];
}

+ (CGImageRef)newImageGradientInRect:(CGRect)rect colors:(NSArray *)colors locations:(NSArray *)locations
{
    int w = CGRectGetWidth(rect);
    int h = CGRectGetHeight(rect);
    int bitsPerComponent = 8;
    int bpp = 4 * bitsPerComponent / 8;
    int byteCount = w * h * bpp;

    int colorCount = (int)colors.count;
    int locationCount = (int)locations.count;
    int* cols = NULL;
    float* locs = NULL;

    if (colorCount > 0) {
        cols = calloc(colorCount, bpp);
        int *p = cols;
        for (id cg in colors) {
            CGColorRef c = BRIDGE_CAST(CGColorRef)cg;
            float r, g, b, a;

            size_t n = CGColorGetNumberOfComponents(c);
            const CGFloat *comps = CGColorGetComponents(c);
            if (comps == NULL) {
                *p++ = 0;
                continue;
            }
            r = comps[0];
            if (n >= 4) {
                g = comps[1];
                b = comps[2];
                a = comps[3];
            }
            else {
                g = b = r;
                a = comps[1];
            }
            *p++ = RGBAF(r, g, b, a);
        }
    }
    if (locationCount > 0 && locationCount == colorCount) {
        locs = calloc(locationCount, sizeof(locs[0]));
        float *p = locs;
        for (NSNumber *n in locations) {
            *p++ = [n floatValue];
        }
    }

    byte* data = malloc(byteCount);
    angleGradient(data, w, h, cols, colorCount, locs, locationCount);

    if (cols) free(cols);
    if (locs) free(locs);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Little;
    CGContextRef ctx = CGBitmapContextCreate(data, w, h, bitsPerComponent, w * bpp, colorSpace, bitmapInfo);
    CGColorSpaceRelease(colorSpace);
    CGImageRef img = CGBitmapContextCreateImage(ctx);
    CGContextRelease(ctx);
    free(data);
    return img;
}

@end

static inline byte blerp(byte a, byte b, float w)
{
    return a + w * (b - a);
}
static inline int lerp(int a, int b, float w)
{
    return RGBA(blerp(RGBA_R(a), RGBA_R(b), w),
                blerp(RGBA_G(a), RGBA_G(b), w),
                blerp(RGBA_B(a), RGBA_B(b), w),
                blerp(RGBA_A(a), RGBA_A(b), w));
}
static inline int multiplyByAlpha(int c)
{
    float a = RGBA_A(c) / 255.0;
    return RGBA((byte)(RGBA_R(c) * a),
                (byte)(RGBA_G(c) * a),
                (byte)(RGBA_B(c) * a),
                RGBA_A(c));
}

void angleGradient(byte* data, int w, int h, int* colors, int colorCount, float* locations, int locationCount)
{
    if (colorCount < 1) return;
    if (locationCount > 0 && locationCount != colorCount) return;

    int* p = (int*)data;
    float centerX = (float)w / 2;
    float centerY = (float)h / 2;

    for (int y = 0; y < h; y++)
    for (int x = 0; x < w; x++) {
        float dirX = x - centerX;
        float dirY = y - centerY;
        float angle = atan2f(dirY, dirX);
        if (dirY < 0) angle += 2 * M_PI;
        angle /= 2 * M_PI;

        int index = 0, nextIndex = 0;
        float t = 0;

        if (locationCount > 0) {
            for (index = locationCount - 1; index >= 0; index--) {
                if (angle >= locations[index]) {
                    break;
                }
            }
            if (index >= locationCount) index = locationCount - 1;
            nextIndex = index + 1;
            if (nextIndex >= locationCount) nextIndex = locationCount - 1;
            float ld = locations[nextIndex] - locations[index];
            t = ld <= 0 ? 0 : (angle - locations[index]) / ld;
        }
        else {
            t = angle * (colorCount - 1);
            index = t;
            t -= index;
            nextIndex = index + 1;
            if (nextIndex >= colorCount) nextIndex = colorCount - 1;
        }

        int lc = colors[index];
        int rc = colors[nextIndex];
        int color = lerp(lc, rc, t);
        color = multiplyByAlpha(color);
        *p++ = color;
    }
}

Ответ 6

Я долго искал, как это сделать, поэтому я думал, что опубликую, как я это сделал. Получается, что оба ответа находятся в отличном ответе на этот вопрос:

Нарисуйте сегменты из круга или пончика

В моих целях я использовал только чертежные и градиентные части этого ответа. Структура выглядит более или менее подобной...

CGContextRef context = UIGraphicsGetCurrentcontext();

CGFloat arcStartAngle = M_PI;
CGFloat arcEndAngle = 2 * M_PI;

CGPoint startPoint = CGPointMake(...);
CGPoint endPoint = CGPointMake(...);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

CGFloat colors[] =
{
    1.0, 0.0, 0.0, 1.0,   //RGBA values (so red to green in this case)
    0.0, 1.0, 0.0, 1.0    
};

CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2);
//Where the 2 is for the number of color components. You can have more colors throughout //your gradient by adding to the colors[] array, and changing the components value.

CGColorSpaceRelease(colorSpace);

//Now for the arc part...

CGMutablePathRef arc = CGPathCreateMutable();

CGPathMoveToPoint(arc, NULL, startPoint.x, startPoint.y);


//Here, the CGPoint self.arcCenter is the point around which the arc is placed, so maybe the
//middle of your view. self.radius is the distance between this center point and the arc.
CGPathAddArc(arc, NULL, self.arcCenter.x, self.arcCenter.y, self.radius, 
             arcStartAngle, arcEndAngle, YES);


//This essentially draws along the path in an arc shape
CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(arc, NULL, 5.0f, 
                                                      kCGLineCapButt, kCGLineJoinMiter, 10);


CGContextSaveGState(context);

CGContextAddPath(context, strokedArc);
CGContextClip(context);

CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);

CGContextDrawPath(context, kCGPathFillStroke);

CGGradientRelease(gradient);
CGContextRestoreGState(context);

//This all draws a gradient that is much larger than the arc itself, but using
//CGContextClip, it clips out everything EXCEPT the colors in the arc. Saving and Restoring
//the state allows you to preserve any other drawing going on. If you didn't use these,
//then all other drawing would also be clipped.

Надеюсь, это поможет. Если это неясно, я рекомендую вам проверить ссылку выше. Ответ на эти вопросы содержит все, что я использовал в этом ответе, и еще несколько интересных и полезных советов по рисованию.

Ответ 7

Я использовал alecail ответ (iOS с блоками), однако я заметил, что ему нужно несколько корректировок (просто добавлено несколько CGPathRelease(), чтобы - (void) drawGratientInContext:...), потому что он использовал слишком много памяти. Извините, я бы опубликовал это как комментарий, но, поскольку у меня нет 50+ репутации, я должен ответить следующим образом:

-(void)drawGradientInContext:(CGContextRef)ctx  startingAngle:(float)a endingAngle:(float)b intRadius:(floatfloatBlock)intRadiusBlock outRadius:(floatfloatBlock)outRadiusBlock withGradientBlock:(floatColorBlock)colorBlock withSubdiv:(int)subdivCount withCenter:(CGPoint)center withScale:(float)scale
{
    float angleDelta = (b-a)/subdivCount;
    float fractionDelta = 1.0/subdivCount;

    CGPoint p0,p1,p2,p3, p4,p5;
    float currentAngle=a;
    p4=p0 = [self pointForTrapezoidWithAngle:currentAngle andRadius:intRadiusBlock(0) forCenter:center];
    p5=p3 = [self pointForTrapezoidWithAngle:currentAngle andRadius:outRadiusBlock(0) forCenter:center];
    CGMutablePathRef innerEnveloppe=CGPathCreateMutable(),
    outerEnveloppe=CGPathCreateMutable();

    CGPathMoveToPoint(outerEnveloppe, 0, p3.x, p3.y);
    CGPathMoveToPoint(innerEnveloppe, 0, p0.x, p0.y);
    CGContextSaveGState(ctx);

    CGContextSetLineWidth(ctx, 1);

    for (int i=0;i<subdivCount;i++)
    {
        float fraction = (float)i/subdivCount;
        currentAngle=a+fraction*(b-a);
        CGMutablePathRef trapezoid = CGPathCreateMutable();

        p1 = [self pointForTrapezoidWithAngle:currentAngle+angleDelta andRadius:intRadiusBlock(fraction+fractionDelta) forCenter:center];
        p2 = [self pointForTrapezoidWithAngle:currentAngle+angleDelta andRadius:outRadiusBlock(fraction+fractionDelta) forCenter:center];

        CGPathMoveToPoint(trapezoid, 0, p0.x, p0.y);
        CGPathAddLineToPoint(trapezoid, 0, p1.x, p1.y);
        CGPathAddLineToPoint(trapezoid, 0, p2.x, p2.y);
        CGPathAddLineToPoint(trapezoid, 0, p3.x, p3.y);
        CGPathCloseSubpath(trapezoid);

        CGPoint centerofTrapezoid = CGPointMake((p0.x+p1.x+p2.x+p3.x)/4, (p0.y+p1.y+p2.y+p3.y)/4);

        CGAffineTransform t = CGAffineTransformMakeTranslation(-centerofTrapezoid.x, -centerofTrapezoid.y);
        CGAffineTransform s = CGAffineTransformMakeScale(scale, scale);
        CGAffineTransform concat = CGAffineTransformConcat(t, CGAffineTransformConcat(s, CGAffineTransformInvert(t)));
        CGPathRef scaledPath = CGPathCreateCopyByTransformingPath(trapezoid, &concat);

        CGContextAddPath(ctx, scaledPath);
        CGContextSetFillColorWithColor(ctx,colorBlock(fraction).CGColor);
        CGContextSetStrokeColorWithColor(ctx, colorBlock(fraction).CGColor);
        CGContextSetMiterLimit(ctx, 0);

        CGContextDrawPath(ctx, kCGPathFillStroke);

        p0=p1;
        p3=p2;

        CGPathAddLineToPoint(outerEnveloppe, 0, p3.x, p3.y);
        CGPathAddLineToPoint(innerEnveloppe, 0, p0.x, p0.y);

        //Release both CGPathRefs. The original code just released 
        //trapezoid CG . 
        CGPathRelease(trapezoid);
        CGPathRelease(scaledPath);
    }
    //stroke width
    CGContextSetLineWidth(ctx, 0.0001);
    CGContextSetLineJoin(ctx, kCGLineJoinRound);
    CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
    CGContextAddPath(ctx, outerEnveloppe);
    CGContextAddPath(ctx, innerEnveloppe);
    CGContextMoveToPoint(ctx, p0.x, p0.y);
    CGContextAddLineToPoint(ctx, p3.x, p3.y);
    CGContextMoveToPoint(ctx, p4.x, p4.y);
    CGContextAddLineToPoint(ctx, p5.x, p5.y);
    CGContextStrokePath(ctx);

    //Release CGPathRefs
    CGPathRelease(innerEnveloppe);
    CGPathRelease(outerEnveloppe);

}