寻找最接近CGPoint的对象

时间:2010-08-23 22:12:25

标签: iphone uiview cgpoint

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

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

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

有什么想法吗?

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

更新

hRuler http://img.skitch.com/20100824-c333thq3rjcak9f5q5djjugjhw.preview.jpg
红色针脚是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??????

4 个答案:

答案 0 :(得分:20)

以下方法应该可以解决问题。如果您发现任何奇怪的内容,请随时指出。

- (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:方法也是为了完整性。

答案 1 :(得分:9)

如果你正在使用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)
    }
}

答案 2 :(得分:2)

谢谢@cristian,

这是你答案的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);
    }
}

答案 3 :(得分:0)

缩短 @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)
    }
}