Как нарисовать линию градиента (исчезновение / исчезновение) с Core Graphics / iPhone? - PullRequest
24 голосов
/ 20 августа 2009

Я знаю, как нарисовать простую линию:

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

И я знаю, как сделать градиентный прямоугольник, например ::1004.

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);

Но как я могу нарисовать линию с градиентом, например переход от черного к белому (и, возможно, переход к черному с другой стороны)?

Ответы [ 5 ]

29 голосов
/ 30 июля 2014

Можно обводить произвольные пути градиентом или любым другим эффектом заливки, например шаблоном.

Как вы обнаружили, обведенные пути не отображаются с текущим градиентом. Только заполненные пути используют градиент (когда вы превращаете их в клип, а затем рисуете градиент).

Однако , в Core Graphics есть удивительно крутая процедура CGContextReplacePathWithStrokedPath, которая преобразует путь, который вы намереваетесь штрих , в путь эквивалент при заполнении .

За кулисами CGContextReplacePathWithStrokedPath строит многоугольник ребер вокруг вашего пути обводки и переключает его на путь, который вы определили. Я бы предположил, что ядро ​​рендеринга Core Graphics, вероятно, делает это в любом случае в вызовах CGContextStrokePath.

Вот документация Apple по этому вопросу:

Кварц создает обведенный путь, используя параметры текущего графического контекста. Новый путь создается таким образом, чтобы при его заполнении отображались те же пиксели, что и при обводке исходного пути. Вы можете использовать этот путь так же, как вы используете путь любого контекста. Например, вы можете привязать к заштрихованной версии пути, вызвав эту функцию с последующим вызовом функции 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
    );

Дополнительные примечания

Стоит подчеркнуть часть документации выше:

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

Очевидным параметром является ширина линии. Тем не менее, используется all состояние рисования линий, такое как рисунок обводки, ограничение угла наклона, соединения линий, ограничения, штриховые рисунки и т. Д. Это делает этот подход чрезвычайно мощным.

Для получения дополнительной информации см. этот ответ из этого S.O. вопрос .

20 голосов
/ 20 августа 2009

После нескольких попыток я теперь уверен, что градиенты не влияют на обводки, поэтому я думаю, что невозможно нарисовать градиентные линии с 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);

и все работает отлично. Спасибо Брэду Ларсону за подсказку.

8 голосов
/ 20 августа 2009

После того, как вы проведете линию, вы можете позвонить

CGContextClip(context);

, чтобы обрезать рисунок в области линии. Если вы рисуете градиент, он должен теперь находиться внутри области линии. Обратите внимание, что вам нужно будет использовать чистый цвет для вашей линии, если вы хотите, чтобы отображался градиент, а не линия под ним.

Существует вероятность, что линия будет слишком тонкой для отображения градиента, и в этом случае вы можете использовать CGContextAddRect() для определения более толстой области.

Я представляю более подробный пример использования этого контекстного отсечения в моем ответе здесь .

8 голосов
/ 20 августа 2009

Вы можете использовать слои Core Animation. Вы можете использовать CAShaperLayer для своей линии, установив ее свойство path, а затем вы можете использовать CAGradientLayer в качестве маски слоя для вашего слоя формы, что приведет к исчезновению линии.

Замените ваши вызовы CGContext ... на вызовы CGPath ..., чтобы создать путь к линии. Установите поле пути на слое, используя этот путь. Затем в градиентном слое укажите цвета, которые вы хотите использовать (возможно, от черного к белому), а затем установите маску в качестве линейного слоя следующим образом:

 [gradientLayer setMask:lineLayer];

Что хорошо в слое градиента, так это то, что он позволяет вам указать список мест, где градиент остановится, так что вы можете постепенно исчезать и исчезать. Он поддерживает только линейные градиенты, но похоже, что он может соответствовать вашим потребностям.

Дайте мне знать, если вам нужны разъяснения.

РЕДАКТИРОВАТЬ: Теперь, когда я думаю об этом, просто создайте один слой CAGradientLayer, который является шириной / высотой желаемой линии. Укажите цвета градиента (от черного к белому или от черного к прозрачному цвету), а также начальную и конечную точки, и это должно дать вам то, что вам нужно.

0 голосов
/ 30 мая 2012
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);

это работа для меня.

...