19

我想在 iOS 中使用核心图形绘制一个像附加图像中的形状。这可能吗。如果可能,请提供示例代码。在此处输入图像描述

我希望形状上至少有 3 个颜色渐变。

4

7 回答 7

47

渐变不会自然地沿着路径绘制。你必须模拟它。该代码使用 NSBezierPath、NSView 和 CGContext,但将其移植到 iOS 应该不难。

我做了梯形的组装。

这是绘制渐变的 NSView 的 drawRect:

@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(内部半径)的各种组合,并且具有不同的比例。

沿圆形渐变

带有块的新版本,iOS 就绪

这个版本使用 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

例子:

曲线渐变 1

曲线渐变 2

于 2012-10-08T05:30:35.413 回答
23

如果你想把它画在CGContext

用于+[UIBezierPath bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:]创建包含半圆弧的路径。然后使用CGPathCreateCopyByStrokingPath在圆弧周围创建一个封闭的形状。用于CGContextAddPath将此封闭形状添加到上下文路径,然后用于CGContextClip将剪辑区域设置为粗弧。

使用您的彩虹色CGGradientCreateWithColors创建一个。CGGradient使用CGContextDrawLinearGradient渐变填充剪辑区域。

如果你想使用 a 来绘制它CALayer

创建一个CAGradientLayer. 将图层的colors属性设置为您的彩虹色。将图层设置startPoint为 (0,0),将图层endPoint设置为 (1,0)。

创建一个CAShapeLayer并将其设置为渐变图层的蒙版。使用+[UIBezierPath bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:]CGPathCreateCopyByStrokingPath创建一个包含粗圆弧的路径,并将此路径设置为形状图层的path.

于 2012-08-02T18:21:04.107 回答
5

从 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 滤镜生成的圆形渐变。

颜色和中心点以CIColors 和CIVectors 封装传入,内圆和外圆的半径以浮点数给出。如果要传入其他参数,当然可以修改参数列表。

这种方法甚至足以用于动画,但如果你想这样做,请确保为每一帧使用相同的上下文,因为创建和拆除它是一个性能瓶颈。只需确保您的动画参数是输入参数并循环执行步骤 4 和 5。

另外,不要忘记最后的调用CGImageRelease,否则你会有内存泄漏。

于 2015-11-30T04:47:06.537 回答
2

我使用了 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);
    }
}
于 2013-05-09T07:35:13.127 回答
2

作为另一个答案,我发现了这个: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;
    }
}
于 2014-02-17T10:44:14.213 回答
1

我也花了很长时间寻找如何做到这一点,所以我想我会发布我最终这样做的方式。事实证明,这两个答案都很好地回答了这个问题:

从圆形或甜甜圈中绘制线段

出于我的目的,我只使用了该答案的绘图和渐变部分。结构看起来或多或少是这样的......

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.

我希望这有帮助。如果其中任何一个不清楚,我建议您查看上面的问题链接。该问题的答案包含我在此答案中使用的所有内容以及一些更酷且有用的绘图技巧。

于 2014-06-03T01:39:56.173 回答
0

我使用了 alecail的答案(iOS with blocks),但是,我注意到它需要一些调整(只是在 -(void) drawGratientInContext:... 中添加了一些 CGPathRelease() ),因为它使用了太多记忆。抱歉,我会将此作为评论发布,但由于我没有 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);

}
于 2015-04-27T17:47:42.070 回答