Scenario:
I have a set of CGPath
s. They are mostly just lines (i.e. not closed shapes). They are drawn on the screen in a UIView
's draw method.
我有一套CGPath。它们大多只是线条(即不是封闭的形状)。它们以UIView的绘制方法在屏幕上绘制。
How can I check if the user tapped near one of the paths?
如何检查用户是否在其中一条路径附近轻敲?
Here's what I had working:
这就是我的工作:
UIGraphincsBeginImageContext(CGPathGetBoundingBox(path));
CGContextRef g = UIGraphicsGetCurrentContext();
CGContextAddPath(g,path);
CGContextSetLineWidth(g,15);
CGContextReplacePathWithStrokedPath(g);
CGPath clickArea = CGContextCopyPath(g); //Not documented
UIGraphicsEndImageContext();
So what I'm doing is creating an image context, because it has the functions I need. I then add the path to the context, and set the line width to 15. Stroking the path at this point would create the click area I can check inside of to find clicks. So I get that stroked path by telling the context to turn the path into a stroked path, then copying that path back out into another CGPath. Later, I can check:
所以我正在做的是创建一个图像上下文,因为它具有我需要的功能。然后我将路径添加到上下文中,并将行宽设置为15.此时描绘路径将创建我可以在其中检查以查找点击的点击区域。所以我通过告诉上下文将路径转换为描边路径,然后将该路径复制回另一个CGPath来获得该描述路径。后来我可以查一下:
if (CGPathContainsPoint(clickArea,NULL,point,NO)) { ...
It all worked well and good, but the CGContextCopyPath
, being undocumented, seemed like a bad idea to use for obvious reasons. There's also a certain kludginess about making a CGContext
just for this purpose.
这一切都运行良好,但CGContextCopyPath,没有记录,似乎是一个坏主意,使用明显的原因。关于为此目的制作CGContext还有一定的瑕疵。
So, does anybody have any ideas? How do I check if a user tapped near (in this case, within 15 pixels) of any area on a CGPath
?
那么,有没有人有任何想法?如何检查用户是否在CGPath的任何区域附近(在这种情况下,在15像素内)轻敲?
2 个解决方案
#1
18
In iOS 5.0 and later, this can be done more simply using CGPathCreateCopyByStrokingPath
:
在iOS 5.0及更高版本中,可以使用CGPathCreateCopyByStrokingPath更简单地完成此操作:
CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(path, NULL, 15,
kCGLineCapRound, kCGLineJoinRound, 1);
BOOL pointIsNearPath = CGPathContainsPoint(strokedPath, NULL, point, NO);
CGPathRelease(strokedPath);
if (pointIsNearPath) ...
#2
2
Well, I figured out an answer. It uses CGPathApply:
好吧,我想出了答案。它使用CGPathApply:
clickArea = CGPathCreateMutable();
CGPathApply(path,clickArea,&createClickArea);
void createClickArea (void *info, const CGPathElement *elem) {
CGPathElementType type = elem->type;
CGMutablePathRef path = (CGMutablePathRef)info;
static CGPoint last;
static CGPoint subpathStart;
switch (type) {
case kCGPathElementAddCurveToPoint:
case kCGPathElementAddQuadCurveToPoint:
break;
case kCGPathElmentCloseSubpath:
case kCGPathElementMoveToPoint: {
CGPoint p = type == kCGPathElementAddLineToPoint ? elem->points[0] : subpathStart;
if (CGPointEqualToPoint(p,last)) {
return;
}
CGFloat rad = atan2(p.y - last.y, p.x - last.x);
CGFloat xOff = CLICK_DIST * cos(rad);
CGFloat yOff = CLICK_DIST * sin(rad);
CGPoint a = CGPointMake(last.x - xOff, last.y - yOff);
CGPoint b = CGPointMake(p.x + xOff, p.y + yOff);
rad += M_PI_2;
xOff = CLICK_DIST * cos(rad);
yOff = CLICK_DIST * sin(rad);
CGPathMoveToPoint(path, NULL, a.x - xOff, a.y - yOff);
CGPathAddLineToPoint(path, NULL, a.x + xOff, a.y + yOff);
CGPathAddLineToPoint(path, NULL, b.x + xOff, b.y + yOff);
CGPathAddLineToPoint(path, NULL, b.x - xOff, b.y - yOff);
CGPathCloseSubpath(path);
last = p;
break; }
case kCGPathElementMoveToPoint:
subpathStart = last = elem->points[0];
break;
}
}
Basically it's just my own method for ReplacePathWithStrokedPath, but it only works with lines for right now.
基本上它只是我自己的ReplacePathWithStrokedPath方法,但它只适用于现在的行。
#1
18
In iOS 5.0 and later, this can be done more simply using CGPathCreateCopyByStrokingPath
:
在iOS 5.0及更高版本中,可以使用CGPathCreateCopyByStrokingPath更简单地完成此操作:
CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(path, NULL, 15,
kCGLineCapRound, kCGLineJoinRound, 1);
BOOL pointIsNearPath = CGPathContainsPoint(strokedPath, NULL, point, NO);
CGPathRelease(strokedPath);
if (pointIsNearPath) ...
#2
2
Well, I figured out an answer. It uses CGPathApply:
好吧,我想出了答案。它使用CGPathApply:
clickArea = CGPathCreateMutable();
CGPathApply(path,clickArea,&createClickArea);
void createClickArea (void *info, const CGPathElement *elem) {
CGPathElementType type = elem->type;
CGMutablePathRef path = (CGMutablePathRef)info;
static CGPoint last;
static CGPoint subpathStart;
switch (type) {
case kCGPathElementAddCurveToPoint:
case kCGPathElementAddQuadCurveToPoint:
break;
case kCGPathElmentCloseSubpath:
case kCGPathElementMoveToPoint: {
CGPoint p = type == kCGPathElementAddLineToPoint ? elem->points[0] : subpathStart;
if (CGPointEqualToPoint(p,last)) {
return;
}
CGFloat rad = atan2(p.y - last.y, p.x - last.x);
CGFloat xOff = CLICK_DIST * cos(rad);
CGFloat yOff = CLICK_DIST * sin(rad);
CGPoint a = CGPointMake(last.x - xOff, last.y - yOff);
CGPoint b = CGPointMake(p.x + xOff, p.y + yOff);
rad += M_PI_2;
xOff = CLICK_DIST * cos(rad);
yOff = CLICK_DIST * sin(rad);
CGPathMoveToPoint(path, NULL, a.x - xOff, a.y - yOff);
CGPathAddLineToPoint(path, NULL, a.x + xOff, a.y + yOff);
CGPathAddLineToPoint(path, NULL, b.x + xOff, b.y + yOff);
CGPathAddLineToPoint(path, NULL, b.x - xOff, b.y - yOff);
CGPathCloseSubpath(path);
last = p;
break; }
case kCGPathElementMoveToPoint:
subpathStart = last = elem->points[0];
break;
}
}
Basically it's just my own method for ReplacePathWithStrokedPath, but it only works with lines for right now.
基本上它只是我自己的ReplacePathWithStrokedPath方法,但它只适用于现在的行。