Подтвердить что ты не робот

Как нарисовать линию градиента (затухание в/из) с помощью 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);

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

4b9b3361

Ответ 1

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

и все работает отлично. Благодаря Брэду Ларсону за решающий намек.

Ответ 2

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

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

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

За кулисами 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
    );

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

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

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

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

Подробнее см. этот ответ этого S.O. вопрос.

Ответ 3

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

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

 [gradientLayer setMask:lineLayer];

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

Сообщите мне, если вам нужно уточнение.

EDIT: Теперь, когда я думаю об этом, просто создайте один CAGradientLayer, который является шириной/высотой линии, которую вы хотите. Укажите цвета градиента (от черного до белого или черного цвета, чтобы очистить цвет), а также startPoint и endtPoints, и он должен предоставить вам то, что вам нужно.

Ответ 4

После рисования линии вы можете вызвать

CGContextClip(context);

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

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

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

Ответ 5

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

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