Search code examples
iosobjective-cuitouchhittestcgpoint

Hit Test to determine which CGPoint was closest to touch - 16 CGPoint dots in 1 UIView


I've been modifying this custom UIView by LMinh called LMGaugeView in order to make it look like a 16-vial circular "vial carousel".

Imagine 16 dots (CGPoints) evenly dispersed around the edge of the circle (UIView). I want to be able to do the following scenario:

Vial Carousel

The picture shows 10 vials, but you get the idea. As soon as I touch the circle view, I want to be able to determine which "vial" I tapped based on their CGPoint value alone.

I created an app (called Twinstones, just to throw that out there) that required the hitTest:withEvent: method, but I was dealing with 2 subviews that could be touched (within the frame of their superview.)

For this, the circle is the only view (which means the hitTest:withEvent: will only return the circle view every time I come in contact with it.)

Here's that hitTest:... implementation:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
        return nil;
    }

    CGRect touchRect = CGRectInset(self.bounds, -14, -14);
    if (CGRectContainsPoint(touchRect, point)) {
       for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
            CGPoint convertedPoint = [subview convertPoint:point fromView:self];
            UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
            if (hitTestView) {
                return hitTestView;
            }
        }
        return self;
    }
    return nil;
}

Is there another hitTest-related method I need to use to get this to work? If you need to see more code, let me know.


Solution

  • The pythagorean theorem is useful here. You can get the point where your user touched the screen, then calculate the distance to each vial with map() and find the smallest value:

    let p1 = //where your user touched the view
    let vialDistances = vials.map { // your vials array
        let p2 = // vial position
        let diffX = p1.x - p2.x
        let diffY = p1.y - p2.y
        return diffX * diffX + diffY * diffY
    }
    let index = find(vialDistances, vialDistances.min())
    let closestVial = vials[index]