touchesBegan in main view - how to get touched subview?


#1

Hello,

I’ve read your book in O’Reilly Safari, it is a nice entry point for me as iOS newbie programmer.

I have the following UIResponder-related question -

In an single view iPhone app I have the following structure: scrollView -> contentView -> imageView and underneath (at the main view) there are 7 draggable custom views - which can be dragged from/to the contenView.

Initially dragging was handled in the custom views (see Tile.m@2eb672523a) and while this worked well it required sending notifications to the main view.

So I’m trying to move touchesBegan and friends to the main app view (see ViewController.m).

My problem is:

Initially I had this code in the contentView (see GameBoard.m) to enable subview dragging at the scrollView:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView* result = [super hitTest:point withEvent:event];

    return result == self ? nil : result;
}

However when trying to drag a tile back from contentView to the main view (in the direction shown in the below screenshot) this wouldn’t deliver touchesBegan to the main view:

So I have changed this method to return the main view instead of the tiles:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView* result = [super hitTest:point withEvent:event];

    return result == self ? nil : self.window.rootViewController.view;
}

Now touchesBegan is called on the main view (as I can see in the debugger), but the touches.view is pointing to the main view too:

2014-04-01 11:05:34.859 ScrollContent[3584:60b] -[ViewController touchesBegan:withEvent:]: 

result=<UIView: 0x8c6bf20; frame = (0 0; 320 480); autoresize = RM+BM; autoresizesSubviews = NO; gestureRecognizers = <NSArray: 0x8c74200>; layer = <CALayer: 0x8c6c100>> 

touch.view=<UIView: 0x8c6bf20; frame = (0 0; 320 480); autoresize = RM+BM; autoresizesSubviews = NO; gestureRecognizers = <NSArray: 0x8c74200>; layer = <CALayer: 0x8c6c100>>
I've tried running a hitTest:withEvent: in the touchesBegan to find the touched tile, but the former method returns the main view too.

Here is my touchesBegan code (see ViewController.m):

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    UITouch* touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.view];
    UIView* result = [self.view hitTest:location withEvent:nil];

    NSLog(@"%s: result=%@ touch.view=%@", __PRETTY_FUNCTION__, result, touch.view);


    if (![result isKindOfClass:[Tile class]])
        return;
    Tile* tile = (Tile*)result;
    tile.alpha = 0;

    _draggedTile = [tile cloneTile];
    _draggedTile.center = [self.view convertPoint:tile.center fromView:tile.superview];

    [self.view addSubview:_draggedTile];
}

My question is:

How to find the touched tile in the above method?

I’ve also tried the following, but the tile is always nil:

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    UITouch* touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.view];

    Tile *tile = nil;
    for (UIView *child in self.view.subviews) {
        if ([child isKindOfClass:[Tile class]] &&
            [child pointInside:location withEvent:event]) {
            tile = (Tile*)child;
            break;
        }
    }

    if (!tile) {
        return;   // XXX unfortunately tile is always nil
    }

    tile.alpha = 0;

    _draggedTile = [tile cloneTile];
    _draggedTile.center = [self.view convertPoint:tile.center fromView:tile.superview];

    [self.view addSubview:_draggedTile];
}

Also I’ve tried a slightly different loop with same result:

  Tile *tile = nil;
    for (UIView *child in self.view.subviews) {
        if ([child isKindOfClass:[Tile class]] &&
            [child pointInside:location withEvent:event]) {
            if ((tile = (Tile*)[child hitTest:location withEvent:event]) != nil) {
                break;
            }
        }
    }

Greetings from Germany
Alex