10

设想:

我有一套CGPaths。它们大多只是线条(即不是封闭的形状)。它们以UIView的 draw 方法绘制在屏幕上。

如何检查用户是否在其中一条路径附近点击?

这是我的工作:

UIGraphincsBeginImageContext(CGPathGetBoundingBox(path));
CGContextRef g = UIGraphicsGetCurrentContext();
CGContextAddPath(g,path);
CGContextSetLineWidth(g,15);
CGContextReplacePathWithStrokedPath(g);
CGPath clickArea = CGContextCopyPath(g);  //Not documented
UIGraphicsEndImageContext();

所以我正在做的是创建一个图像上下文,因为它具有我需要的功能。然后我将路径添加到上下文中,并将线宽设置为 15。此时抚摸路径将创建点击区域,我可以在其中检查以查找点击。因此,我通过告诉上下文将路径转换为描边路径,然后将该路径复制回另一个 CGPath 来获得描边路径。稍后,我可以检查:

if (CGPathContainsPoint(clickArea,NULL,point,NO)) { ...

这一切都运作良好,但由于CGContextCopyPath明显的原因,没有记录的 . 似乎是一个坏主意。CGContext为了这个目的而制作一个公正的东西也有一定的笨拙。

那么,有人有什么想法吗?如何检查用户是否在 a 上的任何区域附近(在本例中为 15 像素内)轻敲CGPath

4

3 回答 3

23

在 iOS 5.0 及更高版本中,这可以更简单地使用CGPathCreateCopyByStrokingPath

CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(path, NULL, 15,
    kCGLineCapRound, kCGLineJoinRound, 1);
BOOL pointIsNearPath = CGPathContainsPoint(strokedPath, NULL, point, NO);
CGPathRelease(strokedPath);

if (pointIsNearPath) ...
于 2012-10-19T17:25:28.420 回答
2

嗯,我想出了一个答案。它使用 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;
  }
}

基本上它只是我自己的 ReplacePathWithStrokedPath 方法,但它现在只适用于线条。

于 2009-07-17T22:34:26.857 回答
0

在斯威夫特

let area = stroke.copy(strokingWithWidth: 15, lineCap: .round, lineJoin: .round, miterLimit: 1)
if (area.contains(point)) { ... }
于 2018-11-29T15:04:54.060 回答