Sine CAShapeLayer as a mask of CALayer

2020-07-16 06:09发布

问题:

I'm trying to implement the next curious thing: I have a few dots and I'd like to link them with sine waves

Next method returns the array of dots for making plot:

- (NSArray *)plotPointsBetweenPoint:(CGPoint)pointA andPoint:(CGPoint)pointB {
  double H = fabs(pointB.y - pointA.y);
  double L = fabs(pointB.x - pointA.x);

  NSInteger granularity = 40;
  NSMutableArray *array = [NSMutableArray arrayWithCapacity:granularity + 1];
  for (int i = 0; i <= granularity; ++i) {
    CGFloat x = M_PI*(float)i/(granularity);
    CGFloat y = -cosf(x);
    CGFloat multiplier = pointA.y > pointB.y ? 1 : -1;
    CGPoint newPoint = CGPointMake(pointA.x + L*(float)i/granularity, pointA.y - multiplier*H*(1.0 + y)/2.0);
    [array addObject:[NSValue valueWithCGPoint:newPoint]];
  }
  return array;
}

After it I create the special UIBezierPath:

- (UIBezierPath *)sineWaveWithPoints:(NSArray *)points {
  UIBezierPath *path = [[UIBezierPath alloc] init];
  NSMutableArray *array = [NSMutableArray array];

  for (int i = 0; i < points.count - 1; ++i) {
    [array addObjectsFromArray:[self plotPointsBetweenPoint:[points[i] CGPointValue] andPoint:[points[i+1] CGPointValue]]];
  }
  [path moveToPoint:[array[0] CGPointValue]];
  for (NSValue *value in array) {
    CGPoint point = [value CGPointValue];
    [path addLineToPoint:point];
  }
  [path closePath];
  path.lineWidth = 2.0;
  [path stroke];
  return path;
}

Next I'm adding this path using CAShapeLayer:

CAGradientLayer *gradientLayer = [self gradientLayer];
CAShapeLayer *maskLine = [CAShapeLayer layer];
maskLine.path = [self sineWaveWithPoints:pointsArray].CGPath;
maskLine.lineWidth = self.plotWidth;
gradientLayer.mask = maskLine;
[self.view.layer addSublayer:gradientLayer];

So I'd like to have gradient sine wave between this points. What exactly is wrong in my actions?

Ideal:

回答1:

The program is behaving correctly - that is, it is doing exactly what you are telling it to do. The problem is that what you are telling it to do (in your code) is not what you say you want (in your words and picture in your question).

First thing you're doing wrong: you said closePath. So it's closing the path! That means that, having drawn the wave, it connects the last point back to the first point with a straight line, giving the shape that you show.

Second thing you're doing wrong: you are failing to manipulate the shape layer. You are making a vanilla CAShapeLayer and leaving it at its defaults. Well, the default is that the fillColor is black (and no strokeColor). You are not changing that. So you are getting your (incorrect) shape filled with black, and no stroke, and hence the resulting mask is the shape of the inside of your (incorrect) shape, giving the gradient mask shape that you show.