首页 文章

将渐变应用于CAShapeLayer

提问于
浏览
30

有没有人有任何将渐变应用于CAShapeLayer的经验? CAShapeLayer是一个很棒的图层类,但它似乎只支持实心填充着色,而我希望它有一个渐变填充(实际上是一个可动画的渐变) .

与CAShapeLayer(阴影,形状,笔触颜色,动画形状路径)相关的所有其他内容都非常棒 .

我已经尝试将CAGradientLayer放在CAShapeLayer中,或者确实将CAShapeLayer设置为GradientLayer的掩码并将两者都添加到容器层,但这些都没有正确的结果 .

我应该继承CAShapeLayer,还是有更好的前进方向?

谢谢 .

3 回答

  • 3

    您可以使用形状的路径来创建蒙版图层并将其应用于渐变图层,如下所示:

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

    如果您还想使用阴影,则必须在渐变图层下放置形状图层的“复制”,以回收相同的路径参考 .

  • 60

    这是一个很好的解决方案,但如果您在CAShapeLayer上创建一个不立即拥有该视图的类别,则可能会遇到意外问题 .

    Setting correct frame of a newly created 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];
    
  • 1

    非常感谢@Palimondo给出了一个很好的答案!

    如果有人正在寻找此解决方案的代码:1010391_ filling animation

    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",请添加以下行:

    // Filling animation
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.duration = 10
        gradientMask.add(animation, forKey: "LineAnimation")
    

相关问题