Применение градиента к CAShapeLayer - PullRequest
31 голосов
/ 19 января 2011

Есть ли у кого-нибудь опыт применения градиента в CAShapeLayer? CAShapeLayer - фантастический класс слоев, но он, кажется, поддерживает только заливку сплошной заливкой, тогда как я хотел бы, чтобы у нее была градиентная заливка (на самом деле это анимируемый градиент).

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

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

Должен ли я подкласс CAShapeLayer, или есть лучший путь вперед?

Спасибо.

Ответы [ 3 ]

60 голосов
/ 01 сентября 2011

Вы можете использовать путь вашей фигуры, чтобы создать маскирующий слой и применить его к градиентному слою, например:

UIView *v = [[UIView alloc] initWithFrame:self.window.frame];

CAShapeLayer *gradientMask = [CAShapeLayer layer];   
gradientMask.fillColor = [[UIColor clearColor] CGColor];
gradientMask.strokeColor = [[UIColor blackColor] CGColor];
gradientMask.lineWidth = 4;
gradientMask.frame = CGRectMake(0, 0, v.bounds.size.width, v.bounds.size.height);

CGMutablePathRef t = CGPathCreateMutable();    
CGPathMoveToPoint(t, NULL, 0, 0);
CGPathAddLineToPoint(t, NULL, v.bounds.size.width, v.bounds.size.height);

gradientMask.path = t;


CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.startPoint = CGPointMake(0.5,1.0);
gradientLayer.endPoint = CGPointMake(0.5,0.0);
gradientLayer.frame = CGRectMake(0, 0, v.bounds.size.width, v.bounds.size.height);
NSMutableArray *colors = [NSMutableArray array];
for (int i = 0; i < 10; i++) {
    [colors addObject:(id)[[UIColor colorWithHue:(0.1 * i) saturation:1 brightness:.8 alpha:1] CGColor]];
}
gradientLayer.colors = colors;

[gradientLayer setMask:gradientMask];
[v.layer addSublayer:gradientLayer];

Если вы также хотите использовать тени, вам нужно будет поместить «дубликат» слоя формы под слой градиента, возвращая ту же ссылку на путь.

3 голосов
/ 08 июля 2018

Большое спасибо @Palimondo за отличный ответ!

В случае, если кто-то ищет Swift 4 + анимация заполнения код этого решения:

    let myView = UIView(frame: .init(x: 0, y: 0, width: 200, height: 150))
    view.addSubview(myView)
    myView.center = view.center

    // Start and finish point
    let startPoint = CGPoint(x: myView.bounds.minX, y: myView.bounds.midY)
    let finishPoint = CGPoint(x: myView.bounds.maxX, y: myView.bounds.midY)

    // Path
    let path = UIBezierPath()
    path.move(to: startPoint)
    path.addLine(to: finishPoint)

    // Gradient Mask
    let gradientMask = CAShapeLayer()
    let lineHeight = myView.frame.height
    gradientMask.fillColor = UIColor.clear.cgColor
    gradientMask.strokeColor = UIColor.black.cgColor
    gradientMask.lineWidth = lineHeight
    gradientMask.frame = myView.bounds
    gradientMask.path = path.cgPath

    // Gradient Layer
    let gradientLayer = CAGradientLayer()
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)

    // make sure to use .cgColor
    gradientLayer.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
    gradientLayer.frame = myView.bounds
    gradientLayer.mask = gradientMask

    myView.layer.addSublayer(gradientLayer)

    // Corner radius
    myView.layer.cornerRadius = 10
    myView.clipsToBounds = true

Extra .Если вам также нужна анимация заполнения, добавьте следующие строки:

    // Filling animation
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.fromValue = 0
    animation.duration = 10
    gradientMask.add(animation, forKey: "LineAnimation")
3 голосов
/ 03 февраля 2014

Это отличное решение, но вы можете столкнуться с неожиданными проблемами, если вы создаете категорию на CAShapeLayer, где у вас не сразу открывается вид.

См. Установка правильного кадра вновь созданного CAShapeLayer

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

// Category on CAShapeLayer

CGRect pathBounds = CGPathGetBoundingBox(self.path);

CAShapeLayer *gradientMask = [CAShapeLayer layer];
gradientMask.fillColor = [[UIColor blackColor] CGColor];
gradientMask.frame = CGRectMake(0, 0, pathBounds.size.width, pathBounds.size.height);
gradientMask.path = self.path;

CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.startPoint = CGPointMake(0.5,1.0);
gradientLayer.endPoint = CGPointMake(0.5,0.0);
gradientLayer.frame = CGRectMake(0, 0, pathBounds.size.width, pathBounds.size.height);

NSMutableArray *colors = [NSMutableArray array];
for (int i = 0; i < 10; i++) {
    [colors addObject:(id)[[UIColor colorWithHue:(0.1 * i) saturation:1 brightness:.8 alpha:1] CGColor]];
}
gradientLayer.colors = colors;

[gradientLayer setMask:gradientMask];
[self addSublayer:gradientLayer];
...