5

I'm using a CAShapeLayer. I'm trying to make it so that its path changes whenever a mouse event occurs. For example, when a mouse down event occurs, a path is appended to a CGMutableRefPath and then then the shape layer's path is set to it.

The problem: When I set the CAShapeLayer path with a new path, the display is not updated with a new path unless I do . How do I tell the system that the shape layer's path has changed and to redraw itself?

Note: When I create a copy of the mutable path and pass it along, it works, but doing a copy every time is not performant and causes my computer to slow down when there are lots of paths.

CGMutablePathRef paths = CGPathCreateMutable();
CAShapeLayer *shapeLayer = [CAShapeLayer layer];

- (void)mouseDown:(NSEvent *)event {
    CGPathAddEllipseInRect(paths, nil, rect);
    self.shapeLayer.path = paths; // Works once, but subsequent calls do nothing
    self.shapeLayer.path = CGPathCreateCopy(paths); // Works, but super slow
}
jrturton
  • 118,105
  • 32
  • 252
  • 268
user2647249
  • 149
  • 1
  • 6

2 Answers2

9

Call [self.shapeLayer didChangeValueForKey:@"path"] to force the layer to redraw itself.

user2647249
  • 149
  • 1
  • 6
  • Ah, sounds like `setPath:` sees that the pointer you're passing is the same as the one the property already has, and doesn't look to see if the path itself has changed (which would be expensive). Your work-around simply forces the action that would have occurred had you passed a different pointer. (Obviously, `CAShapeLayer` is not copying the path, even if it's mutable, but also not updating automatically if that path is changed, which was my own question that led me to this post.) – big_m Dec 27 '13 at 04:58
1

Make sure your path is a valid one i.e. it has valid vertices and fill etc. Such as (this is just an example of a 200 X 200 rect filled with color, you may use your own):

 CGContextRef context = UIGraphicsGetCurrentContext();
 CGContextAddPath(context, paths);
 CGPathAddRect(paths, NULL, CGRectMake(100.0, 100.0, 200.0, 200.0)); // generates a warning specified later
 CGContextFillPath(context);

After updating path, call

[shapeLayer setNeedsDisplay]

or

[shapeLayer setNeedsLayout]

or both.

Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
  • 1
    Thanks, but I've tried calling `setNeedsDisplay` and `setNeedsLayout` on the shape layer, but it has no effect. Pretty sure my paths are all valid. They are all circles with R=50, but different origins. What does work is creating a copy of paths and setting to it. Example: `self.shapeLayer.path = CGPathCreateCopy(paths);`. But doing a copy over time causes my computer to bog down. – user2647249 Aug 02 '13 at 21:07
  • Figured it out. I had to call `[self.shapeLayer didChangeValueForKey:@"path"]` to get the layer to re-render itself. Thanks for the help though. – user2647249 Aug 03 '13 at 00:23
  • If it helped mark is at right (checkmark) - it will help you get quicker answers in future. – Nirav Bhatt Aug 03 '13 at 04:49