2009-07-17 6 views
9

Scenariusz:Jak mogę sprawdzić, czy użytkownik stuknął w pobliżu CGPath?

Mam zestaw CGPath s. Są to w większości tylko linie (tzn. Nie zamknięte kształty). Są rysowane na ekranie w metodzie losowania UIView.

Jak mogę sprawdzić, czy użytkownik stuknął w pobliżu jednej ze ścieżek?

Oto co ja pracuje:

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

Więc co robię jest stworzenie kontekstu obrazu, ponieważ ma funkcje, których potrzebuję. Następnie dodaję ścieżkę do kontekstu i ustawię szerokość linii na 15. Przesuwanie ścieżki w tym miejscu tworzy obszar klikania, który mogę sprawdzić wewnątrz, aby znaleźć kliknięcia. Dostaję więc tę obrysowaną ścieżkę, mówiąc kontekst, aby zmienić ścieżkę w ścieżkę obrysowaną, a następnie skopiować tę ścieżkę z powrotem do innej ścieżki CGPath. Później można sprawdzić:

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

Wszystko działało dobrze, ale CGContextCopyPath, będąc nieudokumentowana, wydawało się, że to zły pomysł, aby korzystać z oczywistych powodów. Istnieje również pewna kufiness w tworzeniu CGContext tylko w tym celu.

Czy ktoś ma jakieś pomysły? Jak sprawdzić, czy użytkownik stuknął w pobliżu (w tym przypadku w obrębie 15 pikseli) dowolnego obszaru na CGPath?

Odpowiedz

18

w iOS 5.0 i później, można to zrobić prościej używając CGPathCreateCopyByStrokingPath:

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

if (pointIsNearPath) ... 
+2

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

2

Cóż, znalazłem odpowiedź. Wykorzystuje 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; 
    } 
} 

Zasadniczo jest to tylko moja własna metoda ReplacePathWithStrokedPath, ale działa tylko z linii do teraz.

+0

Bardzo dziękuję za komentarz to! Ja sam nie miałbym o tym pojęcia. Chociaż zaakceptowana odpowiedź działa dobrze, ale ta funkcja w zasadzie mnie uratowała, ponieważ zapewnia elastyczność - mogę inaczej traktować elementy ścieżki: dokładnie to, czego potrzebowałem. Dzięki jeszcze raz! – user1244109