How do I move a ball along a specific UiBezierPath? Is that possible?
I've tried everything including doing a hit test using
-(BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath*)path inFillArea:(BOOL)inFill
How do I detect a touch along a path?
Swit: for @rakesh's answer
func tapTargetForPath(_ path: CGPath) -> UIBezierPath? {
let path = UIBezierPath(cgPath: path)
guard let tapTargetPath = CGPath(__byStroking: path.cgPath,
transform: nil,
lineWidth: CGFloat(fmaxf(35.0, Float(path.lineWidth))),
lineCap: path.lineCapStyle,
lineJoin: path.lineJoinStyle,
miterLimit: path.miterLimit) else {
return nil
}
return UIBezierPath(cgPath: tapTargetPath)
}
there is a method in bezierpath class called containsPoint: .. refer: http://developer.apple.com/library/ios/#documentation/2ddrawing/conceptual/drawingprintingios/BezierPaths/BezierPaths.html
and you can detect weather the touch point is in bezier path object or not. I have used this with my own method by which a user can easily detect a touch on the bezier path (not inside or out sied if a circle or close path is there).
This code lets user select a bezier path drawing object on touch of it and a dashed line with animation appears on it. hope it helps someone. Here is code from my own project:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if([[self tapTargetForPath:path] containsPoint:startPoint]) // 'path' is bezier path object
{
[self selectShape:currentSelectedPath];// now select a new/same shape
NSLog(@"selectedShapeIndex: %d", selectedShapeIndex);
break;
}
}
// this method will let you easily select a bezier path ( 15 px up and down of a path drawing)
- (UIBezierPath *)tapTargetForPath:(UIBezierPath *)path
{
if (path == nil) {
return nil;
}
CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, fmaxf(35.0f, path.lineWidth), path.lineCapStyle, path.lineJoinStyle, path.miterLimit);
if (tapTargetPath == NULL) {
return nil;
}
UIBezierPath *tapTarget = [UIBezierPath bezierPathWithCGPath:tapTargetPath];
CGPathRelease(tapTargetPath);
return tapTarget;
}
-(void)selectShape:(UIBezierPath *)pathToSelect
{
centerline = [CAShapeLayer layer];
centerline.path = pathToSelect.CGPath;
centerline.strokeColor = [UIColor whiteColor].CGColor;
centerline.fillColor = [UIColor clearColor].CGColor;
centerline.lineWidth = 1.0;
centerline.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:10], [NSNumber numberWithInt:5], nil];
[self.layer addSublayer:centerline];
// showing animation on line
CABasicAnimation *dashAnimation;
dashAnimation = [CABasicAnimation animationWithKeyPath:@"lineDashPhase"];
[dashAnimation setFromValue:[NSNumber numberWithFloat:0.0f]];
[dashAnimation setToValue:[NSNumber numberWithFloat:30.0f]];
[dashAnimation setDuration:1.0f];
[dashAnimation setRepeatCount:10000];
[centerline addAnimation:dashAnimation forKey:@"linePhase"];
}
Firstly ... it is an absolute fact that:
there is, definitely, NO method, provided by Apple,
to extract points from a UIBezierPath.
That is an absolute fact as of February 2011, in the latest OS, and everything just on the horizon. I've spoken to the relevant engineers at Apple about the issue. It's annoying in many game programming contexts, but that's the fact. So, it could be that answers your question?
Secondly: don't forget it's as easy as pie to animate something along a UIBezierPath. There are numerous answers on SO, for example How can I animate the movement of a view or image along a curved path?
Thirdly: Regarding finding out where touches happened, if that's what you're asking, as Cory said, this is a hard problem! But you can use CGContextPathContainsPoint to find out if a point (or touch) was on a path of a given thickness.
After that, assuming we are talking about cubics, you need to find points and likely velocities (a.k.a. tangents) along a bezier.
Here is the exact, total code to do that: Find the tangent of a point on a cubic bezier curve (on an iPhone). I pasted it in below, too.
You will have to implement your own MCsim or iteration to find where it hit, depending on your situation. That's not so hard.
(Fourthly -- as a footnote, there's that new thing where you can progressively draw a bezpath, probably not relevant to your question but just a note.)
For the convenience of anyone reading in the future, here is the summary from the linked question of the two "handy" routines...
Finally, here in the simplest possible fashion are the two routines to calculate equidistant points (in fact, approximately equidistant) and the tangents of those, along a bezier cubic:
CGFloat bezierPoint(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
{
CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
CGFloat C4 = ( a );
return ( C1*t*t*t + C2*t*t + C3*t + C4 );
}
CGFloat bezierTangent(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
{
CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
CGFloat C4 = ( a );
return ( ( 3.0 * C1 * t* t ) + ( 2.0 * C2 * t ) + C3 );
}
The four precalculated values, C1 C2 C3 C4, are sometimes called the coefficients of the bezier. (Recall that a b c d are usually called the four control points.) Of course, t runs from 0 to 1, perhaps for example every 0.05. Simply call these routines once for X and separately once for Y. Hope it helps someone!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With