【问题标题】:Finding closest object to CGPoint寻找离CGPoint最近的物体
【发布时间】:2010-08-23 22:12:25
【问题描述】:

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

在四分位数上,我在每个四分位数上都有一些对象 (UIImageViews)。

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

有什么想法吗?

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

更新:


(来源:skitch.com

红色引脚是 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]);

            }

调试控制台

这就是问题所在。每个瓷砖是 256x256。第一个 UIImageView 的 CGPoint 转换为 UIScrollView 的坐标系 (53.25, 399.36) 应该与 tapPoint (30,331) 保持一致。为什么不一样??点击点右侧的另一个点正在计算更近(距离)??

<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??????

【问题讨论】:

  • 这基本上需要大量的数学运算。你的 UIImageView 大小一样吗?
  • 是的,他们是。 CGRect 26.624 71.68 32 39。大小都一样。
  • 添加了 Image 和 Kalle 代码的更新。当我将 UIImageView 的 CGPoint 转换为 UIScrollViews 坐标系时,发生了一些奇怪的事情,这会抛出坐标和 Kalle 的代码。谁能看到我错过了什么?
  • 嗯。对于初学者,尝试在 distanceBetweenRect:andPoint: 中添加一个 NSLog 来打印出正在比较的两个点?即: NSLog(@"计算 %f,%f 和 %f,%f. 之间的距离。", point.x,point.y, nearest.x,closest.y);就在回报之上。如果这看起来很奇怪并且输入是正确的,那么我的答案一定搞砸了:O
  • 我尝试将您的 tapLocation 和框架手动放入,并添加了调试信息。我得到:计算 30.000000,332.000000 和 30.000000,110.680000 之间的距离。 || distance => 221.320007 -- 对我来说看起来是正确的。

标签: iphone uiview cgpoint


【解决方案1】:

以下方法应该可以解决问题。如果您发现其中有任何奇怪之处,请随时指出。

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

那里可能有更优化的版本,所以可能值得更多挖掘。

以下方法确定两个CGPoints之间的距离。

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

更新:删除 fabsf(); -x^2 与 x^2 相同,所以不需要。

更新 2:为了完整起见,也添加了 distanceBetweenPoint:andPoint: 方法。

【讨论】:

  • 好的,这很有趣。让我发布一些代码,将对象 CGPoint 转换为 UIScrollView 的坐标,以便 Kalle 的代码可以工作。我注意到一些奇怪的行为......
  • 很高兴报告,SOVLED!我正在使用 [subview convertPoint:subview.frame.origin toView:self];而不是 [[subview superview] convertPoint:subview.frame.origin toView:self];回到我的原始代码,一切正常。但是你帮我追踪了错误。完美的!谢谢卡勒!!我欠你一个!
  • 很高兴听到 :) 出于好奇,您最初是如何解决的?
  • 这是我之前解决的方法,但是因为我没有正确的“视图”,所以数字被关闭了。我喜欢你的方法更好。 -(CGFloat)distanceBetweenPoints:(CGPoint)pt1 和:(CGPoint)pt2 { CGFloat xdist = pt2.x - pt1.x; CGFloat ydist = pt2.y - pt1.y; CGFloat 距离 = sqrt((xdist * xdist) + (ydist * ydist));返回距离; }
  • 啊,是的,如果用错误的观点,任何方法都会产生奇怪的结果。 :P 我认为您可能不需要像我那样走得太远,因为您的应用程序是关于点击引脚的;它似乎不需要那种准确性。
【解决方案2】:

如果您使用 Swift,以下是计算 CGPoint 和 CGRect 之间距离的方法(例如 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)
    }
}

【讨论】:

    【解决方案3】:

    谢谢@克里斯蒂安,

    这是您答案的 Objective-C 版本

    - (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】:

      @cristian 更短的答案:

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

      【讨论】:

        猜你喜欢
        • 2011-02-02
        • 2018-01-15
        • 1970-01-01
        • 2011-03-20
        • 2023-03-10
        • 2014-10-20
        • 2022-01-14
        • 2020-10-02
        • 1970-01-01
        相关资源
        最近更新 更多