I have a UIView which is added as a subview to my view controller. I have drawn a bezier path on that view. My drawRect implementation is below
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *bpath = [UIBezierPath bezierPath];
[bpath moveToPoint:CGPointMake(50, 50)];
[bpath addLineToPoint:CGPointMake(100, 50)];
[bpath addLineToPoint:CGPointMake(100, 100)];
[bpath addLineToPoint:CGPointMake(50, 100)];
[bpath closePath];
CGContextAddPath(context, bpath.CGPath);
CGContextSetStrokeColorWithColor(context,[UIColor blackColor].CGColor);
CGContextSetLineWidth(context, 2.5);
CGContextStrokePath(context);
UIColor *fillColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.5 alpha:0.7];
[fillColor setFill];
[bpath fill];
}
I want detect tap inside this bezier path but not the point which is inside the UIView and outside the path. For example in this case if my touch coordinate is (10, 10), it should not be detected. I know about CGContextPathContainsPoint but it does not help when touch is inside the path. Is there a way to detect touch events inside bezier path?
There is a function CGPathContainsPoint()
it may be useful in your case.
Also be careful if you get gesture point from superview, the coordinate may not be correct with your test. You have a method to convertPoint from or to a particular view's coordinate system:
- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view
- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view
Try UIBezierPath's method :
func contains(_ point: CGPoint) -> Bool
Returns a Boolean value indicating whether the area enclosed by the
receiver contains the specified point.
Detecting tap inside a bezier path in swift :-
It's simple in latest swift ,follow these steps and you will get your UIBezierPath touch event.
Step 1 :- Initialize Tap Event on view where your UIBeizerPath Added.
///Catch layer by tap detection
let tapRecognizer:UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(YourClass.tapDetected(_:)))
viewSlices.addGestureRecognizer(tapRecognizer)
Step 2 :- Make "tapDetected" method
//MARK:- Hit TAP
public func tapDetected(tapRecognizer:UITapGestureRecognizer){
let tapLocation:CGPoint = tapRecognizer.locationInView(viewSlices)
self.hitTest(CGPointMake(tapLocation.x, tapLocation.y))
}
Step 3 :- Make "hitTest" final method
public func hitTest(tapLocation:CGPoint){
let path:UIBezierPath = yourPath
if path.containsPoint(tapLocation){
//tap detected do what ever you want ..;)
}else{
//ooops you taped on other position in view
}
}
Update: Swift 4
Step 1 :- Initialize Tap Event on view where your UIBeizerPath Added.
///Catch layer by tap detection
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourClass.tapDetected(tapRecognizer:)))
viewSlices.addGestureRecognizer(tapRecognizer)
Step 2 :- Make "tapDetected" method
public func tapDetected(tapRecognizer:UITapGestureRecognizer){
let tapLocation:CGPoint = tapRecognizer.location(in: viewSlices)
self.hitTest(tapLocation: CGPoint(x: tapLocation.x, y: tapLocation.y))
}
Step 3 :- Make "hitTest" final method
private func hitTest(tapLocation:CGPoint){
let path:UIBezierPath = yourPath
if path.contains(tapLocation){
//tap detected do what ever you want ..;)
}else{
//ooops you taped on other position in view
}
}
A solution in Swift 3.1 (porting over the Apple recommended solution from here)
func containsPoint(_ point: CGPoint, path: UIBezierPath, inFillArea: Bool) -> Bool {
UIGraphicsBeginImageContext(self.size)
let context: CGContext? = UIGraphicsGetCurrentContext()
let pathToTest = path.cgPath
var isHit = false
var mode: CGPathDrawingMode = CGPathDrawingMode.stroke
if inFillArea {
// check if UIBezierPath uses EO fill
if path.usesEvenOddFillRule {
mode = CGPathDrawingMode.eoFill
} else {
mode = CGPathDrawingMode.fill
}
} // else mode == stroke
context?.saveGState()
context?.addPath(pathToTest)
isHit = (context?.pathContains(point, mode: mode))!
context?.restoreGState()
return isHit
}