【问题标题】:How can I check if a user tapped near a CGPath?如何检查用户是否在 CGPath 附近点击?
【发布时间】:2009-07-17 14:41:42
【问题描述】:

场景:

我有一组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 也有一定的笨拙。

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

【问题讨论】:

    标签: iphone quartz-graphics


    【解决方案1】:

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

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

    【讨论】:

    • 在 Swift 3.0 中:path.copy(strokingWithWidth: CGFloat … )
    【解决方案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 方法,但它现在只适用于线条。

    【讨论】:

    • 非常感谢您发布此信息!我自己对此一无所知。虽然接受的答案工作正常,但这个功能基本上救了我,因为它给了你灵活性——我可以不同地对待路径元素:正是我需要的。再次感谢!
    【解决方案3】:

    在斯威夫特中

    let area = stroke.copy(strokingWithWidth: 15, lineCap: .round, lineJoin: .round, miterLimit: 1)
    if (area.contains(point)) { ... }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-26
      • 2013-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多