2010-08-23 4 views
9

Ho quattro UIViews su un (schermo diviso in quartili) UIScrollViewFinding oggetto più vicino al CGPoint

Sulle quartili, ho un paio di oggetti (UIImageViews), su ogni quartile.

Quando l'utente tocca lo schermo, voglio trovare l'oggetto più vicino al CGPoint specificato?

Qualche idea?

Ho il CGPoint e il frame (CGRect) degli oggetti all'interno di ciascun quartile.

UPDATE:

hRuler http://img.skitch.com/20100824-c333thq3rjcak9f5q5djjugjhw.preview.jpg
Pins rosso sono UIImageViews.
// UIScrollView 
    NSLog(@" UIScrollView: %@", self); 

    // Here's the tap on the Window in UIScrollView's coordinates 
    NSLog(@"TapPoint: %3.2f, %3.2f", tapLocation.x, tapLocation.y); 

    // Find Distance between tap and objects 
    NSArray *arrayOfCGRrectObjects = [self subviews]; 
    NSEnumerator *enumerator = [arrayOfCGRrectObjects objectEnumerator]; 

    for (UIView *tilesOnScrollView in enumerator) { 

     // each tile may have 0 or more images 
     for (UIView *subview in tilesOnScrollView.subviews) { 

      // Is this an UIImageView? 
      if ([NSStringFromClass([subview class]) isEqualToString:@"UIImageView"]) { 

       // Yes, here are the UIImageView details (subView) 
       NSLog(@"%@", subview); 

       // Convert CGPoint of UIImageView to CGPoint of UIScrollView for comparison... 
       // First, Convert CGPoint from UIScrollView to UIImageView's coordinate system for reference 
       CGPoint found = [subview convertPoint:tapLocation fromView:self]; 
       NSLog(@"Converted Point from ScrollView: %3.2f, %3.2f", found.x, found.y); 

       // Second, Convert CGPoint from UIScrollView to Window's coordinate system for reference 
       found = [subview convertPoint:subview.frame.origin toView:nil]; 
       NSLog(@"Converted Point in Window: %3.2f, %3.2f", found.x, found.y); 

       // Finally, use the object's CGPoint in UIScrollView's coordinates for comparison 
       found = [subview convertPoint:subview.frame.origin toView:self]; // self is UIScrollView (see above) 
       NSLog(@"Converted Point: %3.2f, %3.2f", found.x, found.y); 

       // Determine tap CGPoint in UIImageView's coordinate system 
       CGPoint localPoint = [touch locationInView:subview]; 
       NSLog(@"LocateInView: %3.2f, %3.2f",localPoint.x, localPoint.y); 

       //Kalle's code 
        CGRect newRect = CGRectMake(found.x, found.y, 32, 39); 
        NSLog(@"Kalle's Distance: %3.2f",[self distanceBetweenRect:newRect andPoint:tapLocation]); 

      } 

console debug

Ecco il problema. Ogni riquadro è 256x256. Il primo CGPoint di UIImageView convertito nel sistema di coordinate di UIScrollView (53.25, 399.36) dovrebbe essere spento con tapPoint (30,331). Perché la differenza ?? L'altro punto a destra del punto toccato sta calcolando più vicino (distanza saggio) ??

<CALayer: 0x706a690>> 
[207] TapPoint: 30.00, 331.00 
[207] <UIImageView: 0x7073db0; frame = (26.624 71.68; 32 39); opaque = NO; userInteractionEnabled = NO; tag = 55; layer = <CALayer: 0x70747d0>> 
[207] Converted Point from ScrollView: 3.38, 3.32 
[207] Converted Point in Window: 53.25, 463.36 
[207] Converted Point: 53.25, 399.36 *** Looks way off! 
[207] LocateInView: 3.38, 3.32 
[207] Kalle's Distance: 72.20 **** THIS IS THE TAPPED POINT 
[207] <UIImageView: 0x7074fb0; frame = (41.984 43.008; 32 39); opaque = NO; userInteractionEnabled = NO; tag = 55; layer = <CALayer: 0x7074fe0>> 
[207] Converted Point from ScrollView: -11.98, 31.99 
[207] Converted Point in Window: 83.97, 406.02 
[207] Converted Point: 83.97, 342.02 
[207] LocateInView: -11.98, 31.99 
207] Kalle's Distance: 55.08 ***** BUT THIS ONE's CLOSER?????? 
+0

Questo è fondamentalmente andando richiedere un po 'di matematica. Le tue UIImageViews hanno le stesse dimensioni? –

+0

Sì, lo sono. CGRect 26.624 71.68 32 39. Tutte le stesse dimensioni. – Jordan

+0

Aggiunto aggiornamento con codice Image e Kalle. C'è qualcosa di strano quando converto il CGPoint di UIImageView nel sistema di coordinate UIScrollViews che butta via le coordinate e il codice di Kalle. Qualcuno può vedere cosa mi manca? – Jordan

risposta

20

Il seguente metodo dovrebbe fare il trucco. Se noti qualcosa di strano in esso, sentiti libero di indicarlo.

- (CGFloat)distanceBetweenRect:(CGRect)rect andPoint:(CGPoint)point 
{ 
    // first of all, we check if point is inside rect. If it is, distance is zero 
    if (CGRectContainsPoint(rect, point)) return 0.f; 

    // next we see which point in rect is closest to point 
    CGPoint closest = rect.origin; 
    if (rect.origin.x + rect.size.width < point.x) 
     closest.x += rect.size.width; // point is far right of us 
    else if (point.x > rect.origin.x) 
     closest.x = point.x; // point above or below us 
    if (rect.origin.y + rect.size.height < point.y) 
     closest.y += rect.size.height; // point is far below us 
    else if (point.y > rect.origin.y) 
     closest.y = point.y; // point is straight left or right 

    // we've got a closest point; now pythagorean theorem 
    // distance^2 = [closest.x,y - closest.x,point.y]^2 + [closest.x,point.y - point.x,y]^2 
    // i.e. [closest.y-point.y]^2 + [closest.x-point.x]^2 
    CGFloat a = powf(closest.y-point.y, 2.f); 
    CGFloat b = powf(closest.x-point.x, 2.f); 
    return sqrtf(a + b); 
} 

uscita Esempio:

CGPoint p = CGPointMake(12,12); 

CGRect a = CGRectMake(5,5,10,10); 
CGRect b = CGRectMake(13,11,10,10); 
CGRect c = CGRectMake(50,1,10,10); 
NSLog(@"distance p->a: %f", [self distanceBetweenRect:a andPoint:p]); 
// 2010-08-24 13:36:39.506 app[4388:207] distance p->a: 0.000000 
NSLog(@"distance p->b: %f", [self distanceBetweenRect:b andPoint:p]); 
// 2010-08-24 13:38:03.149 app[4388:207] distance p->b: 1.000000 
NSLog(@"distance p->c: %f", [self distanceBetweenRect:c andPoint:p]); 
// 2010-08-24 13:39:52.148 app[4388:207] distance p->c: 38.013157 

Ci potrebbe essere più ottimizzato versioni là fuori, quindi potrebbe essere la pena scavare più.

Il seguente metodo determina la distanza tra due punti CGP.

- (CGFloat)distanceBetweenPoint:(CGPoint)a andPoint:(CGPoint)b 
{ 
    CGFloat a2 = powf(a.x-b.x, 2.f); 
    CGFloat b2 = powf(a.y-b.y, 2.f); 
    return sqrtf(a2 + b2) 
} 

Aggiornamento: rimosso fabsf(); -x^2 è uguale a x^2, quindi non è necessario.

Aggiornamento 2: aggiunto il metodo distanceBetweenPoint:andPoint:, per completezza.

+0

Ok, questo sta diventando interessante. Permettetemi di postare un po 'di codice che converta gli oggetti CGPoint nelle coordinate di UIScrollView in modo che il codice di Kalle funzioni. Ho notato un comportamento strano ... – Jordan

+0

Felice di segnalare, SOVLED! Stavo usando [subview convertPoint: subview.frame.origin toView: self]; invece di [[subview superview] convertPoint: subview.frame.origin toView: self]; Torna al mio codice originale e tutto ha funzionato bene. Ma mi hai aiutato a tenere traccia dell'errore. Perfezionare! Grazie Kalle !! Ti devo un favore! – Jordan

+0

Ottimo da sentire :) Per curiosità, come lo hai originariamente risolto? – Kalle

8

Se si utilizza Swift, ecco come è possibile calcolare la distanza tra un CGPoint e un CGRect (ad es.cornice di un UIView)

private func distanceToRect(rect: CGRect, fromPoint point: CGPoint) -> CGFloat { 
    // if it's on the left then (rect.minX - point.x) > 0 and (point.x - rect.maxX) < 0 
    // if it's on the right then (rect.minX - point.x) < 0 and (point.x - rect.maxX) > 0 
    // if it's inside the rect then both of them < 0. 
    let dx = max(rect.minX - point.x, point.x - rect.maxX, 0) 
    // same as dx 
    let dy = max(rect.minY - point.y, point.y - rect.maxY, 0) 
    // if one of them == 0 then the distance is the other one. 
    if dx * dy == 0 { 
     return max(dx, dy) 
    } else { 
     // both are > 0 then the distance is the hypotenuse 
     return hypot(dx, dy) 
    } 
} 
1

Grazie @cristian,

Ecco Objective-C versione della vostra risposta

- (CGFloat)distanceToRect:(CGRect)rect fromPoint:(CGPoint)point 
{ 
    CGFloat dx = MAX(0, MAX(CGRectGetMinX(rect) - point.x, point.x - CGRectGetMaxX(rect))); 
    CGFloat dy = MAX(0, MAX(CGRectGetMinY(rect) - point.y, point.y - CGRectGetMaxY(rect))); 

    if (dx * dy == 0) 
    { 
     return MAX(dx, dy); 
    } 
    else 
    { 
     return hypot(dx, dy); 
    } 
} 
0

Shorter @cristian risposta:

func distance(from rect: CGRect, to point: CGPoint) -> CGFloat { 
    let dx = max(rect.minX - point.x, point.x - rect.maxX, 0) 
    let dy = max(rect.minY - point.y, point.y - rect.maxY, 0) 
    return dx * dy == 0 ? max(dx, dy) : hypot(dx, dy) 
} 

Personalmente, Lo implementerei come estensione CGPoint:

extension CGPoint { 
    func distance(from rect: CGRect) -> CGFloat { 
     let dx = max(rect.minX - x, x - rect.maxX, 0) 
     let dy = max(rect.minY - y, y - rect.maxY, 0) 
     return dx * dy == 0 ? max(dx, dy) : hypot(dx, dy) 
    } 
} 

In alternativa, è anche possibile implementarlo come estensione CGRect:

extension CGRect { 
    func distance(from point: CGPoint) -> CGFloat { 
     let dx = max(minX - point.x, point.x - maxX, 0) 
     let dy = max(minY - point.y, point.y - maxY, 0) 
     return dx * dy == 0 ? max(dx, dy) : hypot(dx, dy) 
    } 
}