Horizontal UIScrollView having vertical UIScrollViews inside - how to prevent scrolling of inner scroll views when scrolling outer horizontal view?

眉间皱痕 提交于 2019-12-21 02:01:06

问题


couldn't find a solution for that.

I am building an app, with big scroll view, who has paging (Horizontal). Inside this scroll view, there is a grid of UIView's, and an UIScrollview inside each one of them, with vertical scroll view.

Now, the point is, When I'm paging my 'big' scrollview, sometimes the touch get stuck in one of small scrollviews inside the UIViews of the grid.

I don't know how to avoid it - tried trick with hitTest but still couldn't find the answer.

Hope i'm clear...

Thanks for your help.

Edit:

This is the bigger scrollview:

@implementation UIGridScrollView
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    self.pagingEnabled;
    return self;
}
@end

Now, to this UIGridScroll View, I added as a subview this view:

@implementation UINoteView
{
IBOutlet UIScrollView *_innerScrollView; // this scrollview frame is in the size of the all UINoteView
}

- (void)awakeFromNib
{
    _innerScrollView.contentSize = CGSizeMake(_innerScrollView.frame.size.width, _innerScrollView.frame.size.height+50.0f);
}
@end

The paging works well, the inner scroll view works well, but too many times when I paging the bigger note view, my finger 'get stuck' in the _innerScrollView.

Thanks!


回答1:


@stanislaw, I've just tried the solution you suggest on an iPhone device.

I see your problem.

Your code does prevent occasional scrolls of vertical views but I believe it is not the simultaneous gesture recognition does the job - comment the entire code you provide for inner views and use the code for the outer scroll view with the following modification:

@interface OuterHorizontalScrollView : UIScrollView ...
@property (weak) InnerVerticalScrollView *currentActiveView; // Current inner vertical scroll view displayed.
@end

@implementation OuterHorizontalScrollView
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (self.currentActiveView.dragging == NO) {
        self.currentActiveView.scrollEnabled = NO; // The presence of this line does the job
    }
    return YES;
}

- (void)scrollViewDidEndDragging:(PlacesScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    self.currentActiveView.scrollEnabled = YES; // This is very likely should be done for all subviews, not only a current.
}    
@end



回答2:


You could subclass UIScrollView and implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:, allowing the scroll view's built-in panGestureRecognizer to recognize simultaneously with another scroll view's gesture recognizers.

Example:

//This is needed because the public UIScrollView headers
//don't declare the UIGestureRecognizerDelegate protocol:
@interface UIScrollView (GestureRecognition) <UIGestureRecognizerDelegate>
@end

@interface MyScrollView : UIScrollView

@end

@implementation MyScrollView

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if (gestureRecognizer == self.panGestureRecognizer) {
        return YES;
    } else if ([UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
        //Note: UIScrollView currently doesn't implement this method, but this may change...
        return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
    }
    return NO; //the default
}

@end

Using this subclass for either the horizontal, or all vertical scroll views should suffice.

You might actually find that you like the default behavior better, after you've tried using it this way. Allowing both views to scroll simultaneously will almost always lead to accidental vertical scrolling while swiping left and right, which can be irritating (most people don't do a perfectly horizontal swipe gesture).




回答3:


I'm a little big confused on your setup but you might want to look into -[UIScrollView setDelaysContentTouches:]. You can use it to add a bit of precedence when dealing with nested scroll views.




回答4:


Once i have faced this situation & i have the following work around to done this.

Subclass both of your view from UIScrollView & you will be fine with this. In your case i can see that your UINoteView is not subclass from UIScrollView, subclass it from ScrollView & remove the innerScrollView from this class.




回答5:


Here is how I have done it:

Using shouldRecognizeSimultaneouslyWithGestureRecognizer (thanks @omz!) and custom swipe gesture recognizer in vertical scroll views (see the similar question

Horizontal scrolling UIScrollView with vertical pan gesture), I have the following setup:

@interface UIScrollView (GestureRecognition) <UIGestureRecognizerDelegate>
@end

@interface OuterHorizontalScrollView : UIScrollView ...
@property (weak) InnerVerticalScrollView *currentView; // Current inner vertical scroll view displayed.
@end

@implementation OuterHorizontalScrollView
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (self.currentActiveView.placeVerticalScrollView.dragging == NO) {
        self.currentActiveView.placeVerticalScrollView.scrollEnabled = NO;
        return YES;
    } else {
        return NO;
    }
}    
@end

@interface InnerVerticalScrollView : UIScrollView...
@property UISwipeGestureRecognizer *swipeGestureRecognizer;
@end

@implementation InnerVerticalScrollView
- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        ...
        self.swipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeGesture:)];
        self.swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp;
        [self addGestureRecognizer:self.swipeGestureRecognizer];
    }

    return self;
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer == self.panGestureRecognizer && self.scrollEnabled == YES) {
        return YES;
    } else if (gestureRecognizer == self.swipeGestureRecognizer) {
        return YES;
    } else {
        self.scrollEnabled = NO;
    }

    return NO;
}

- (void)handleSwipeGesture:(UIGestureRecognizer *)sender {
    self.scrollEnabled = YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {    
    return YES;
}

This code is a bit hacky: it allows scrolling of vertical scroll views only if their custom Swipe gesture is recognized (only top or bottom directions), all other gestures are passed to outer scroll view which in its turn allows any gestures only if no one of inner scroll views are being dragged.

Note: it was not obvious that swipe gesture works for slow or tiny swipes too, but it does (see also comments to quoted question above).

I feel like it could be accomplished easier and maybe I will refactor it later.

Anyway, now outer scroll works perfectly - it scrolls horizontally without any occasional vertical pans of inner scroll views.

LATER UPDATE: As I expected before, my solution did contain unnecessary bits of code: see the answer by @burik, while being partially based on my solution, it takes these bits out of it.




回答6:


I think using velocity to determine the scroll direction is a better way:

- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
    CGPoint velocity = [gestureRecognizer velocityInView:gestureRecognizer.view];
    return fabs(velocity.y) > 3 * fabs(velocity.x);
}


来源:https://stackoverflow.com/questions/13272860/horizontal-uiscrollview-having-vertical-uiscrollviews-inside-how-to-prevent-sc

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