使用iOS中的核心图形绘制带有渐变的外半圆?

San*_* MS 19 iphone core-graphics ios

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

我希望在形状上至少有3种颜色渐变.

ale*_*ail 46

渐变不会自然地沿着路径绘制.你必须模拟它.代码使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这看起来像这样,具有各种组合subdivr(内半径),并且具有不同的比例.

沿着一个圆圈的渐变

带有块的新版本,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
Run Code Online (Sandbox Code Playgroud)

例子:

弯曲的梯度1

弯曲的梯度2

  • 如果可以的话,我会支持这10倍.谢谢! (2认同)
  • 这个节省了我的生命+1000 (2认同)

rob*_*off 22

如果你想在CGContext:

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

使用CGGradientCreateWithColors创建一个CGGradient与你的彩虹色.用于使用CGContextDrawLinearGradient渐变填充剪辑区域.

如果你想使用以下方法绘制CALayer:

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

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


del*_*rux 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);
Run Code Online (Sandbox Code Playgroud)

输出如下所示:

使用Core Image过滤器生成的圆形渐变.

颜色和中心点以CIColors和CIVectors的形式传递,内圈和外圈的半径以浮点形式给出.如果要传入其他参数,您当然可以修改参数列表.

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

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