web-dev-qa-db-ja.com

CAShapeLayerへのグラデーションの適用

CAShapeLayerにグラデーションを適用した経験はありますか? CAShapeLayerは素晴らしいレイヤークラスですが、塗りつぶしの塗りつぶしのみをサポートしているように見えますが、グラデーション塗りつぶし(実際にはアニメーション化可能なグラデーション)が必要です。

CAShapeLayerで行うその他すべて(シャドウ、シェイプ、ストロークカラー、アニメート可能なシェイプパス)は素晴らしいものです。

CAGradientLayerをCAShapeLayer内に配置するか、実際にCAShapeLayerをGradientLayerのマスクとして設定し、両方をコンテナーレイヤーに追加しようとしましたが、これらには正しい結果がありません。

CAShapeLayerをサブクラス化する必要がありますか、それともより良い方法がありますか?

ありがとう。

33
user366126

次のように、形状のパスを使用してマスキングレイヤーを作成し、グラデーションレイヤーに適用できます。

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

シャドウも使用する場合は、シェイプレイヤーの「複製」をグラデーションレイヤーの下に配置し、同じパス参照をリサイクルする必要があります。

61
Palimondo

@Palimondoに素晴らしい回答をありがとう!

誰かがSwift 4 + 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

追加。 「塗りつぶしアニメーション」も必要な場合は、次の行を追加します。

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

これは優れたソリューションですが、すぐにビューがない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];
3
user3265536