web-dev-qa-db-ja.com

Core Graphics / iPhoneでグラデーションライン(フェードイン/フェードアウト)を描画する方法は?

私は簡単な線を引く方法を知っています:

CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextMoveToPoint(context, x, y);
CGContextAddLineToPoint(context, x2, y2);
CGContextStrokePath(context);

そして、私はグラデーション長方形を行う方法を知っています、例えば:

CGColorSpaceRef myColorspace=CGColorSpaceCreateDeviceRGB();
size_t num_locations = 2;
CGFloat locations[2] = { 1.0, 0.0 };
CGFloat components[8] = { 0.0, 0.0, 0.0, 1.0,    1.0, 1.0, 1.0, 1.0 };

CGGradientRef myGradient = CGGradientCreateWithColorComponents(myColorspace, components, locations, num_locations);

CGPoint myStartPoint, myEndPoint;
myStartPoint.x = 0.0;
myStartPoint.y = 0.0;
myEndPoint.x = 0.0;
myEndPoint.y = 10.0;
CGContextDrawLinearGradient (context, myGradient, myStartPoint, myEndPoint, 0);

しかし、どうすればグラデーションで線を描くことができますか?黒から白にフェードインしますか(反対側も黒にフェードアウトします)。

24
Walchy

何度か試した結果、グラデーションがストロークに影響を及ぼさないことがわかったので、CGContextStrokePath()を使用してグラデーションラインを描画することは不可能だと思います。水平線と垂直線の場合の解決策は、代わりにCGContextAddRect()を使用することです。交換しました

CGContextMoveToPoint(context, x, y);
CGContextAddLineToPoint(context, x2, y2);
CGContextStrokePath(context);

CGContextSaveGState(context);
CGContextAddRect(context, CGRectMake(x, y, width, height));
CGContextClip(context);
CGContextDrawLinearGradient (context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);

そして、すべてが正常に動作します。重要なヒントを提供してくれたBrad Larsonに感謝します。

20
Walchy

任意のパスをグラデーションまたはパターンなどの他の塗りつぶし効果でストロークすることができます。

あなたが見つけたように、ストロークパスは現在のグラデーションではレンダリングされません。塗りつぶされたパスのみがグラデーションを使用します(クリップに変換してからグラデーションを描画する場合)。

ただし、Core Graphicsには驚くほどクールな手順がありますCGContextReplacePathWithStrokedPath意図したパスを変換しますストローク次のパスに変換しますは入力時と同等です。

裏側では、CGContextReplacePathWithStrokedPathはストロークパスの周囲にエッジポリゴンを構築し、定義したパスに切り替えます。 Core GraphicsレンダリングエンジンはCGContextStrokePathの呼び出しでおそらく anyway を実行していると思います。

これに関するAppleのドキュメントは次のとおりです。

Quartzは、現在のグラフィックスコンテキストのパラメーターを使用してストロークパスを作成します。新しいパスが作成されるので、塗りつぶすと元のパスをなぞるのと同じピクセルが描画されます。このパスは、コンテキストのパスを使用するのと同じ方法で使用できます。たとえば、この関数を呼び出してから関数CGContextClipを呼び出すことで、ストロークのパスのバージョンにクリップできます。

したがって、パスを塗りつぶし可能なものに変換し、それをクリップに変換して、そして/グラデーションを描きます。グラデーションでパスをストロークしたような効果が得られます。

コード

こんな感じになります…

    // Get the current graphics context.
    //
    const CGContextRef context = UIGraphicsGetCurrentContext();

    // Define your stroked path. 
    //
    // You can set up **anything** you like here.
    //
    CGContextAddRect(context, yourRectToStrokeWithAGradient);

    // Set up any stroking parameters like line.
    //
    // I'm setting width. You could also set up a dashed stroke
    // pattern, or whatever you like.
    //
    CGContextSetLineWidth(context, 1);

    // Use the magical call.
    //
    // It turns your _stroked_ path in to a **fillable** one.
    //
    CGContextReplacePathWithStrokedPath(context);

    // Use the current _fillable_ path in to define a clipping region.
    //
    CGContextClip(context);

    // Draw the gradient.
    //
    // The gradient will be clipped to your original path.
    // You could use other fill effects like patterns here.
    //
    CGContextDrawLinearGradient(
      context, 
      yourGradient, 
      gradientTop, 
      gradientBottom, 
      0
    );

その他のメモ

上記のドキュメントの一部を強調する価値があります。

Quartzはストロークパスを作成します現在のグラフィックスコンテキストのパラメーターを使用して

明白なパラメータはライン幅です。ただし、ストロークパターン、マイターリミット、ライン結合、キャップ、ダッシュパターンなどのall線描画状態が使用されます。これにより、このアプローチは非常に強力になります。

詳細については、 この回答このS.O.質問 を参照してください。

31
Benjohn

線を引いた後、

_CGContextClip(context);
_

さらに描画を線領域にクリップします。グラデーションを描画すると、ライン領域内に含まれるようになります。グラデーションを表示したいだけで、その下のラインを表示したくない場合は、ラインにクリアカラーを使用する必要があることに注意してください。

グラデーションを表示するにはラインが細すぎる可能性があります。その場合は、CGContextAddRect()を使用してより太い領域を定義できます。

私はこのコンテキストクリッピングを使用したより複雑な例を私の回答 here で示します。

8
Brad Larson

Core Animationレイヤーを使用できます。パスプロパティを設定することにより、ラインに CAShaperLayer を使用できます。次に、ラインをフェードさせるシェイプレイヤーのレイヤーマスクとして CAGradientLayer を使用できます。

CGContext ...呼び出しをCGPath ...呼び出しに置き換えて、ラインパスを作成します。そのパスを使用してレイヤーのパスフィールドを設定します。次に、グラデーションレイヤーで、使用する色(おそらく黒から白)を指定し、次のようにマスクをラインレイヤーに設定します。

 [gradientLayer setMask:lineLayer];

グラデーションレイヤーの優れている点は、グラデーションが停止する場所のリストを指定できるため、フェードインおよびフェードアウトできることです。線形グラデーションのみをサポートしますが、ニーズに合うかもしれません。

詳細が必要な場合はお知らせください。

編集:私が考えたところで、希望する線の幅/高さである単一のCAGradientLayerを作成します。グラデーションカラー(黒から白または黒からクリアカラー)とstartPointおよびendtPointsを指定すると、必要なものが得られます。

8
Matt Long
CGContextMoveToPoint(context, frame.size.width-200, frame.Origin.y+10);
CGContextAddLineToPoint(context, frame.size.width-200, 100-10);
CGFloat colors[16] = { 0,0, 0, 0,
    0, 0, 0, .8,
    0, 0, 0, .8,
    0, 0,0 ,0 };
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 4);

CGContextSaveGState(context);
CGContextAddRect(context, CGRectMake(frame.size.width-200,10, 1, 80));
CGContextClip(context);
CGContextDrawLinearGradient (context, gradient, CGPointMake(frame.size.width-200, 10), CGPointMake(frame.size.width-200,80), 0);
CGContextRestoreGState(context);

私にとってはその仕事。

0
Bimawa