Draggable UIView stops posting touchesBegan after being added to UIScrollView

ぐ巨炮叔叔 提交于 2019-12-10 07:52:57

问题


In Xcode 5.1 I have created a simple test app for iPhone:

The structure is: scrollView -> contentView -> imageView -> image 1000 x 1000 on the top.

And on the bottom of the single view app I have seven draggable custom UIViews.

The dragging is implemented in Tile.m with touchesXXXX methods.

My problem is: once I add a draggable tile to the contentView in my ViewController.m file - I can not drag it anymore:

- (void) handleTileMoved:(NSNotification*)notification {
    Tile* tile = (Tile*)notification.object;
    //return;

    if (tile.superview != _scrollView && CGRectIntersectsRect(tile.frame, _scrollView.frame)) {
        [tile removeFromSuperview];
        [_contentView addSubview:tile];
        [_contentView bringSubviewToFront:tile];
    }
}

The touchesBegan isn't called for the Tile anymore as if the scrollView would mask that event.

I've searched around and there was a suggestion to extend the UIScrollView class with the following method (in my custom GameBoard.m):

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

    NSLog(@"%s: %hhd", __PRETTY_FUNCTION__,
          [result.superview isKindOfClass:[Tile class]]);

    self.scrollEnabled = ![result.superview isKindOfClass:[Tile class]];
    return result;
}

Unfortunately this doesn't help and prints 0 in debugger.


回答1:


The problem is, partly, because user interactions are disabled on the content view. However, enabling user interactions disables scrolling as the view captures all touches. So here is the solution. Enable user interactions in storyboard, but subclass the content view like so:

@interface LNContentView : UIView

@end

@implementation LNContentView

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

    return result == self ? nil : result;
}

@end

This way, hit test passes only if the accepting view is not self, the content view.

Here is my commit: https://github.com/LeoNatan/ios-newbie




回答2:


The reason Tile views don't get touches is that scroll view's pan gesture recogniser consumes the events. What you need is, attach a UIPanGestureRecongnizer to each of your tiles and configure them as follows:

UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; // handle drag in pan:method
[tile addGestureRecognizer:pan];
UIPanGestureRecognizer *scrollPan = self.scrollView.panGestureRecognizer;
[scrollPan requireGestureRecognizerToFail:pan];

Here you let scroll view's pan gesture recogniser know that you only wish scrolling to happen if none of the tiles are bing dragged.

I've checked the approach — it does work indeed. Regarding your code, you'll need to handle all touches in the gesture recogniser rather than Tile view because touch events may be consumed/delayed by hit-tested view's gesture recogniser before they reach the view itself. Please refer to UIGestureRecognizer documentation to learn more about the topic.




回答3:


It looks as ir one of the views in the hierarchy is capturing the events.

Have a look at the section

The Responder Chain Follows a Specific Delivery Path

Of the Apple doc's here

Edit:

Sorry I was writing from memory. This is how i resolved a similar issue in an app of myself:

I use UITapGestureRecognizer in the view(s) that I want to detect the touch. Implement the following delegate method of the UITapGestureRecognizer:

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event

The touches' set contains all the objects (views) that received the event.



来源:https://stackoverflow.com/questions/22440327/draggable-uiview-stops-posting-touchesbegan-after-being-added-to-uiscrollview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!