2009-07-17 3 views
9

Scénario:Comment puis-je vérifier si un utilisateur a été engagé près d'un CGPath?

J'ai un ensemble de CGPath s. Ce sont principalement des lignes (c'est-à-dire des formes non fermées). Ils sont dessinés à l'écran dans une méthode de dessin de UIView.

Comment puis-je vérifier si l'utilisateur a tapé près de l'un des chemins?

Voici ce que j'avais travail:

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

Alors ce que je fais est la création d'un contexte d'image, car il a les fonctions dont j'ai besoin. Ensuite, j'ajoute le chemin au contexte, et définissez la largeur de la ligne à 15. Caresser le chemin à ce stade créerait la zone de clic, je peux vérifier à l'intérieur de trouver des clics. Donc, je reçois ce chemin caressé en disant au contexte de transformer le chemin en un chemin strié, puis en recopiant ce chemin dans un autre CGPath. Plus tard, je peux vérifier:

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

Tout a bien fonctionné et bien, mais le CGContextCopyPath, étant sans papier, semblait être une mauvaise idée d'utiliser pour des raisons évidentes. Il y a aussi une certaine facilité à créer un CGContext juste à cet effet.

Alors, est-ce que quelqu'un a des idées? Comment puis-je vérifier si un utilisateur a tapé près (dans ce cas, dans les 15 pixels) de n'importe quelle zone sur un CGPath?

Répondre

18

Dans iOS 5.0 et versions ultérieures, cela peut se faire plus simplement à l'aide CGPathCreateCopyByStrokingPath:

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

if (pointIsNearPath) ... 
+2

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

2

Eh bien, j'ai trouvé une réponse. Il utilise 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; 
    } 
} 

Fondamentalement, il est juste ma propre méthode pour ReplacePathWithStrokedPath, mais il fonctionne uniquement avec des lignes pour le moment.

+0

Merci beaucoup pour l'affichage ça! Je n'aurais pas la moindre idée à ce sujet. Bien que la réponse acceptée fonctionne correctement, cette fonction m'a essentiellement sauvé car elle vous donne de la flexibilité - je peux traiter les éléments de chemin différemment: exactement ce dont j'avais besoin. Merci encore! – user1244109