2009-07-17 13 views
9

Escenario:¿Cómo puedo verificar si un usuario tocó cerca de un CGPath?

Tengo un conjunto de CGPath s. En su mayoría son solo líneas (es decir, formas no cerradas). Se dibujan en la pantalla en el método de sorteo UIView.

¿Cómo puedo comprobar si el usuario ha tocado cerca de una de las rutas?

Aquí es lo que había trabajo:

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

Así que lo que estoy haciendo es crear un marco de imagen, ya que tiene las funciones que necesito. A continuación, agrego la ruta al contexto y establezco el ancho de la línea en 15. Al tocar la ruta en este punto crearé el área de clic en la que puedo consultar para encontrar los clics. Así que obtengo ese camino trazado diciéndole al contexto que convierta la ruta en una ruta contorneada, luego copie esa ruta nuevamente en otra CGPath. Más tarde, puedo comprobar:

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

Todo funcionó muy bien, pero el CGContextCopyPath, no tener papeles, parecía una mala idea utilizar por razones obvias. También hay una cierta duda sobre hacer un CGContext solo para este propósito.

Entonces, ¿alguien tiene alguna idea? ¿Cómo puedo verificar si un usuario ha tocado cerca (en este caso, dentro de 15 píxeles) de cualquier área en un CGPath?

Respuesta

18

En iOS 5.0 y versiones posteriores, esto se puede hacer más simple uso de CGPathCreateCopyByStrokingPath:

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

if (pointIsNearPath) ... 
+2

En Swift 3.0: 'path.copy (strokingWithWidth: CGFloat ...)' – buildsucceeded

2

Bueno, descubrí una respuesta. Utiliza 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; 
    } 
} 

Básicamente es sólo mi propio método para ReplacePathWithStrokedPath, pero sólo funciona con las líneas para este momento.

+0

Muchas gracias por publicar esto! Yo mismo no tendría ni idea sobre eso. Aunque la respuesta aceptada funciona bien, pero esta función básicamente me salvó, ya que le da flexibilidad: puedo tratar los elementos de la ruta de manera diferente: exactamente lo que necesitaba. ¡Gracias de nuevo! – user1244109

Cuestiones relacionadas