1

Core Graphics を使用してアプリでいくつかの図形を描画しようとしていますが、複数の図形を 1 つにマスクするためのベスト プラクティスを理解するのに問題があります。

これが最終的になりたい形です(色を含む)。

サークル http://www.myquapps.com/help/lvl1Circle1.png

これまでのところ、次のように正しいストロークで同じサイズの円を描きました:-

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextClearRect(ctx, rect);
    CGContextSetRGBFillColor(ctx, 0, 0, 0, 1);
    CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
    CGContextSetStrokeColor(ctx, red);
    CGContextSetLineWidth(ctx, 18);
    CGContextStrokeEllipseInRect(ctx, CGRectMake(9, 9, 232, 232));
    CGContextFillPath(ctx);

ここで何か助けていただければ幸いです。これは Core Graphics を使用する初めての体験であり、まさに私が必要としているもののようです!

4

2 に答える 2

4

少し時間がかかりましたが、ここに進みます(すべてのベジェパスはインスタンス変数です):

編集:円の内側を透明にしましたが、あなた自身を支えてください、これは醜い速くなります!

#define LARGE_RADIUS 200
#define SMALL_RADIUS 160

#define RADIANS(degrees) ((degrees) / (180.0 / M_PI))

@implementation MyView
{
    UIBezierPath *largeCircle;
    UIBezierPath *orangeWedge;
    UIBezierPath *yellowWedge;
    UIBezierPath *greenWedge;
    UIBezierPath *whiteCircle;

    UIImage *image;
    void *_imageData;
}

-(void) createPaths {
    largeCircle = [UIBezierPath bezierPathWithArcCenter:self.center radius:LARGE_RADIUS startAngle:0 endAngle:RADIANS(360) clockwise:YES];
    whiteCircle = [UIBezierPath bezierPathWithArcCenter:self.center radius:SMALL_RADIUS startAngle:0 endAngle:RADIANS(360) clockwise:YES];    

    orangeWedge = [UIBezierPath bezierPath];
    [orangeWedge moveToPoint:self.center];

    double startAngle = RADIANS(45);
    double endAngle = RADIANS(135);

    [orangeWedge addLineToPoint:CGPointMake(sin(startAngle) + self.center.x, cos(startAngle) + self.center.y)];
    [orangeWedge addArcWithCenter:self.center radius:LARGE_RADIUS startAngle:startAngle endAngle:endAngle clockwise:YES];
    [orangeWedge addLineToPoint:self.center];

    startAngle = RADIANS(60);
    endAngle = RADIANS(120);

    yellowWedge = [UIBezierPath bezierPath];
    [yellowWedge moveToPoint:self.center];

    [yellowWedge addLineToPoint:CGPointMake(sin(startAngle) + self.center.x, cos(startAngle) + self.center.y)];
    [yellowWedge addArcWithCenter:self.center radius:LARGE_RADIUS startAngle:startAngle endAngle:endAngle clockwise:YES];
    [yellowWedge addLineToPoint:self.center];

    startAngle = RADIANS(75);
    endAngle = RADIANS(105);

    greenWedge = [UIBezierPath bezierPath];
    [greenWedge moveToPoint:self.center];

    [greenWedge addLineToPoint:CGPointMake(sin(startAngle) + self.center.x, cos(startAngle) + self.center.y)];
    [greenWedge addArcWithCenter:self.center radius:LARGE_RADIUS startAngle:startAngle endAngle:endAngle clockwise:YES];
    [greenWedge addLineToPoint:self.center];
}

-(void) drawPaths
{
    [[UIColor blackColor] setStroke];
    [[UIColor redColor] setFill];
    [largeCircle fill];
    [largeCircle stroke];
    [[UIColor orangeColor] setFill];
    [orangeWedge fill];
    [orangeWedge stroke];
    [[UIColor yellowColor] setFill];
    [yellowWedge fill];
    [yellowWedge stroke];
    [[UIColor greenColor] setFill];
    [greenWedge fill];
    [greenWedge stroke];

    [whiteCircle stroke];
}

-(int32_t *) pixelAt:(int) x :(int) y
{
    return &((int32_t *)_imageData)[x + (y * (int) self.frame.size.width)];
}

-(void) removeInnerCircle
{
    int32_t invisible = 0;

    int centerX = self.center.x;
    int centerY = self.center.y;

    for (int i = SMALL_RADIUS - 1; i > 0; i--) {
        float incr = (22.0 / i);

        for (float j = 0; j <= 360; j += incr) {
            float angle = RADIANS(j);

            *[self pixelAt:(sin(angle) * i + centerX) :(cos(angle) * i + centerY)] = invisible;
        }
    }
}

-(void) setUpContext
{
    size_t width  = self.frame.size.width;
    size_t height = self.frame.size.height;

    _imageData = malloc(4 * width * height);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(_imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(colorSpace);
    CGContextTranslateCTM(context, 0, height);
    CGContextScaleCTM(context, 1, -1);

    assert(context != NULL);

    UIGraphicsPushContext(context);

    CGContextRelease(context);
}

-(void) destroyContext
{
    CGImageRef imageRef = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());

    assert(imageRef != NULL);

    image = [UIImage imageWithCGImage:imageRef];

    UIGraphicsPopContext();

    free(_imageData);
    _imageData = NULL;
}

-(void) awakeFromNib
{    
    [self createPaths];

    [self setUpContext];

    [self drawPaths];
    [self removeInnerCircle];

    [self destroyContext];    
}

-(void) drawRect:(CGRect)rect
{
    [image drawInRect:self.frame];
}

@end

の値によっては、 inSMALL_RADIUSの増分を変更して、必要以上のループを実行しないようにすることができます。j-removeInnerCircle

現在の半径に基づいて増分するようにコードを変更したjため、中心に近づくほど、変更する必要のあるピクセルが少なくなります。

于 2012-05-09T17:51:50.903 に答える
2

円のパスを作成し、太い線の幅を設定してから、を使用して、パスをリング形状自体に設定しCGContextReplacePathWithStrokedPath()ます。これにより、事実上2つの同心円である新しいパスが作成されます。

CGContextBeginPath(ctx);
CGRect rect = CGRectMake(9, 9, 232, 232);
CGContextAddEllipseInRect(ctx, CGRectInset(rect, 16, 16));
CGContextSetLineWidth(ctx, 32);
CGContextReplacePathWithStrokedPath(ctx);

これで、リングの黒いエッジの線の太さと色を設定して、ストロークすることができます。これにより、内側と外側の両方の黒い同心円が得られます。

CGContextSetLineWidth(ctx, 32);
CGContextSetGrayStrokeColor(ctx, 0, 1);
CGContextStrokePath(ctx);

塗りつぶしの色を設定して塗りつぶします。

CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);
CGContextFillPath(ctx);

今それにクリップします:

CGContextClip(ctx);

これで、リング内にクリップされるオレンジ、黄色、および緑の形状を描画できます。リチャードのコードがそれらの形を完全に正しくするかどうかは私にはわかりません。彼はリングの中心を中心にウェッジを使用していますが、あなたの写真では、エッジがすべて中心を指しているようには見えません。(また、彼は白い円を描いて内部を「消去」しますが、画像は内部で透明です。)クリッピングを設定すると、三角形など、エッジを自分の方向に傾けるのに最も便利なものを描くことができます。それらが欲しいと、それらは自動的にリングに拘束されます。

于 2012-05-09T18:26:32.320 に答える