Search code examples
objective-ccore-animationuibezierpathcgpathcashapelayer

A way to track strokeEnd point of a CAShapeLayer?


My question concerns the possibility of tracking the coordinates of the strokeEnd point of a CAShapeLayer.

I have an UISlider, which generates a variable between 0 and 1. I Create an UIBezierPath from a CGMutablePathRef and I assign it as the path property of an CAShapeLayer.

Here is my header file:

@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UISlider *progressSlider;
@property (strong,nonatomic) CAShapeLayer *raceTrack;
- (IBAction)progressChanged:(id)sender;
@end

In the implementation file, I wrote the following code:

CGMutablePathRef truePath = CGPathCreateMutable();
CGPathMoveToPoint(truePath, NULL, 100.0f, 468.0f-20.0f);
CGPathAddQuadCurveToPoint(truePath, NULL, 70.0f, 268.0f, 100.0f, 68.0f+20.0f);

UIBezierPath *trackPath = [UIBezierPath bezierPathWithCGPath:truePath];

self.raceTrack = [CAShapeLayer layer];
self.raceTrack.path = trackPath.CGPath;
self.raceTrack.strokeEnd = 0.0;
self.raceTrack.strokeColor = [UIColor yellowColor].CGColor;
self.raceTrack.fillColor = [UIColor clearColor].CGColor;
self.raceTrack.lineWidth = 7.0;
[self.view.layer addSublayer:self.raceTrack];

And each time the slider is changed the following function is called:

- (IBAction)progressChanged:(id)sender{
self.raceTrack.strokeEnd = self.progressSlider.value;}

This works perfectly ! But now I want to add a kind of circle at the end of line drawn on the screen. I cannot post image, but if you want to understand we may assume that the circle is a comet and behind it, the stroked path is the tail of the comet.

And I don't know how to achieve this. I thought maybe the best way was to track the coordinates of the stroke end point and add a circle with addSublayer and set the position property of this circle equals to the coordinates of the strokeEnd point. I don't know if a such strokeEnd point exists or maybe there is a simple solution one of you used for this kind of problem.

This why I am asking for your help for the first time.


Solution

  • As far as I know, you have to calculate the point from the formula for the quadratic bezier curve. This code, will do that,

    - (IBAction)progressChanged:(UISlider *)sender{
        self.raceTrack.strokeEnd = sender.value;
        CGPoint strokeEnd = [self pointAtStrokeEnd:sender.value]; 
    }
    
    
    -(CGPoint)pointAtStrokeEnd:(CGFloat) fraction {
    
        CGFloat x = (1 - fraction) * (1 - fraction) * self.startPoint.x + 2 * (1 - fraction) * fraction * self.controlPoint.x + fraction * fraction * self.endPoint.x;
        CGFloat y = (1 - fraction) * (1 - fraction) * self.startPoint.y + 2 * (1 - fraction) * fraction * self.controlPoint.y + fraction * fraction * self.endPoint.y;
        return CGPointMake(x, y);
    }
    

    You'll need to create properties or ivars for the starting, ending, and control point of your curve so you can pass them to the pointAtStrokeEnd: method.