I am making an iOS
drawing app and would like to add functionality where erasing a UIBezierPath
will erase the whole path and not just overlay the segment with a white color.
I have an array of UIBezierPaths
stored, but I am not sure how efficient it is to loop through the entire array for each touch point, detect if it intersects with the current touch point, and remove it from the array.
Any suggestions?
If I understand correctly you need to loop through path at least. They are independent after all.
An UIBezierPaths
has method contains
which would tell you if a point is inside the path. If that is usable then what you need to do is simply
paths = paths.filter { !$0.contains(touchPoint) }
But since this is drawing app it is safe to assume you are using stroke and not fill which will most likely not work with contains
the way you want it to.
You could do the intersection manually and it should have relatively good performance up until you have too many points. But when you have too many points I wager drawing performance will be more of your concern. Still the intersection may be a bit problematic with including some stroke line width; user would need to cross the center of your line to detect a hit.
There is a way though to convert stroked path to filled path. Doing that will then enable you to use contains
method. The method is called replacePathWithStrokedPath
but the thing is that it only exists on CGContext
(At least I have never managed to find an equivalent on UIBezierPath
). So procedure to do so includes creating a context. For instance something like this will work:
static func convertStrokePathToFillPath(_ path: UIBezierPath) throws -> UIBezierPath {
UIGraphicsBeginImageContextWithOptions(CGSize(width: 1.0, height: 1.0), true, 1.0)
guard let context = UIGraphicsGetCurrentContext() else { throw NSError(domain: "convertStrokePathToFillPath", code: 500, userInfo: ["dev_message":"Could not generate image context"]) }
context.addPath(path.cgPath)
context.setLineWidth(path.lineWidth)
// TODO: apply all possible settings from path to context
context.replacePathWithStrokedPath()
guard let returnedPath = context.path else { throw NSError(domain: "convertStrokePathToFillPath", code: 500, userInfo: ["dev_message":"Could not get path from context"]) }
UIGraphicsEndImageContext()
return UIBezierPath(cgPath: returnedPath)
}
So the result should look something like:
static func filterPaths(_ paths: [UIBezierPath], containingPoint point: CGPoint) -> [UIBezierPath] {
return paths.filter { !(try! convertStrokePathToFillPath($0).contains(point)) }
}