代码之家  ›  专栏  ›  技术社区  ›  Jordan

寻找最接近CGPoint的对象

  •  9
  • Jordan  · 技术社区  · 14 年前

    我在UIScrollView上有四个UIView(屏幕分为四分位)

    当用户点击屏幕时,我想找到离给定点最近的对象?

    我有每个四分位数内对象的CGPoint和frame(CGRect)。

    更新:

    hRuler
    (来源: skitch.com )

    红色的图钉是图像视图。
        // 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]);
    
                }
    

    <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??????
    
    4 回复  |  直到 5 年前
        1
  •  21
  •   Kalle    14 年前

    下面的方法应该可以做到这一点。如果你发现里面有什么奇怪的东西,尽管指出。

    - (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);
    }
    

    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
    

    - (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)
    }
    

    Update:removed fabsf();-x^2与x^2相同,因此没有必要。

    distanceBetweenPoint:andPoint: 方法也一样,为了完整性。

        2
  •  11
  •   Ismail    8 年前

    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)
        }
    }
    
        3
  •  2
  •   ilnar_al    8 年前

    谢谢@cristian,

    - (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);
        }
    }
    
        4
  •  0
  •   Federico Zanetello    8 年前

    更短的 @克里斯蒂安

    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)
    }
    

    就我个人而言,我会将此作为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)
        }
    }
    

    或者,也可以将其实现为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)
        }
    }