UIScrollView inside UIScrollView

I have a UIScrollView with another UIScrollView inside. They both are scrolled horizontally and have pagingEnabled = YES.
Assume that I started to scroll inner scroll view and reached the most right bound. And if I proceed scrolling in it, then the outer scrollView begins to move. I need to avoid this. Inner view should jump with rubber-band effect, outer should stay at it’s place.

Hope it’s clear, but here is a sketch:
enter image description here

  • Blurry UILabel when added programmatically
  • How we can add additional UILabels to UINavigation?
  • Error compiling storyboard files using Xcode 6 beta 5
  • Swift Function call list incorrect parameter type
  • Sending Emoji in Push Notifications via PHP on iOS
  • iOS Share GIF (animated image) not Working
  • I’ve tried to set outerView.scrollEnabled = NO; like this:

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
        outerView.scrollEnabled = NO;
    }
    

    , and it works exactly how I need, if to scroll just in innerView. OuterView is not scrolled anymore. But I have to set scrollEnabled back to YES somewhere for the case if I’d want to scroll outerView again.
    I’ve tried to do it here:

    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        outerView.scrollEnabled = YES;
    }
    

    , but than I’m getting the same problem: after reaching the the most right bound of innerView outerView scrolls instead of innerView jumps with rubber-band effect.

    Any suggestions how to solve a problem?

    4 Solutions Collect From Internet About “UIScrollView inside UIScrollView”

    UPDATE

    This solution works always:

    @implementation InnerScrollViewController <UIScrollViewDelegate, UIGestureRecognizerDelegate>
    
    - (void)viewDidLoad
    {
        UISwipeGestureRecognizer*   swipeGesture = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)] autorelease];
        swipeGesture.delegate = self;
        [self.view addGestureRecognizer:swipeGesture];
    }
    
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
    {
        _outerScrollView.scrollEnabled = NO;
        return YES;
    }
    
    - (void)handleSwipe:(UIGestureRecognizer*)gestureRecognizer
    {
    
    }
    
    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {   
        _outerScrollView.scrollEnabled = NO;
    }
    
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
        if (!decelerate)
        {
            _outerScrollView.scrollEnabled = YES;
        }
    }
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        _outerScrollView.scrollEnabled = YES;
    }
    
    @end
    

    ———————————————————————–

    OLD ANSWER: doesn’t work always

    Here is how I solved the problem:

    @implementation InnerView
    
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            self.delaysContentTouches = NO;
        }
        return self;
    }
    
    - (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view
    {
        return NO;
    }
    

    As I understand, self.delaysContentTouches = NO; makes all events to be delivered immediately, and - (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view prevents passing of these events by responder chain.

    Modify your methods by below way:

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
        if(scrollView == innerView)
            outerView.scrollEnabled = NO;
        else
            outerView.scrollEnabled = YES;
    }
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        if(scrollView == innerView)
        {
             if(innerView.contentOffset.x + innerView.frame.size.width == innerView.contentSize.width)
             {
                  outerView.scrollEnabled = NO;
             }
             else
             {
                  outerView.scrollEnabled = YES;
             }
        }
    }
    

    This works well for me:

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
      outerScrollView.scrollEnabled = NO;
    }
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
      [NSTimer scheduledTimerWithTimeInterval:0.3
                                         target:[NSBlockOperation blockOperationWithBlock:^{ outerScrollView.scrollEnabled = YES; }]
                                       selector:@selector(main)
                                       userInfo:nil
                                        repeats:NO];
    }
    

    Remember to adopt the UIScrollViewDelegate in your class declaration and to set the delegate of the innerScrollView to self (do NOT assign the delegate of the outerScrollView)

    I have a similar issue. I found the solution, but it works only with innerScrollView.bounces = NO.

    @interface PageCtrl ()
        @property (weak, nonatomic) IBOutlet UIScrollView *innerScrollView;
        @property(nonatomic) UIPanGestureRecognizer *panLockScroll;
    @end
    
    @implementation PageCtrl
    - (void)viewDidLoad {
        // ...
        self.innerScrollView.delegate = self;   
    
        self.innerScrollView.bounces = NO;   // disable bounces!
    
        self.panLockScroll = [[UIPanGestureRecognizer alloc]initWithTarget:self action:nil];
        self.panLockScroll.delegate = self;
        [self.innerScrollView addGestureRecognizer:self.panLockScroll];
    }
    
    - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer{
        if( gestureRecognizer == self.panLockScroll && 
            otherGestureRecognizer == self.innerScrollView.panGestureRecognizer ){
            return YES;
        }
        return NO;
    }
    
    @end