Nested UIScrollViews and event routing
Asked Answered
B

6

13

I have 2 scroll views, both of which are supposed to scroll vertically. The outer scroll view (red) contains a search bar and the inner scroll view (blue). The inner scroll view is supposed to scroll infinitely (it contains images/items and has an endless scrolling implementation).

The way I want this controller to work is as follows:

When I scroll down, the outer scroll view should scroll first and the search bar should disappear (scroll out of the content area). Only after that the inner scroll view should start scrolling. When scrolling back up, the inner scroll view should scroll all the way to its top. Only then the outer scroll view should take the scroll events and finally scroll up to make the search bar visible again.

If I just nest them in IB without any modifications the inner scroll view catches all the scroll events and it works the other way around.

Please bear in mind that I'm using the inner scroll view as a simplifying metaphor here. In my app I actually have a control here, which has a scroll view with nested table views (scroll view lets me page horizontally, table views let me scroll vertically).

enter image description here

Brookbrooke answered 19/4, 2013 at 11:24 Comment(1)
did you ever find a solution to this, or a workaround?Emulsifier
M
2

If you are able to, set a common UIScrollViewDelegate on the 2 scroll views, and implement the following:

- (void) scrollViewDidScroll: (UIScrollView*) scrollView
{
  if (scrollView == self.mainScrollView)
  {
    /* Handle instances when the main scroll view is already at the bottom */
    if (   scrollView.contentOffset.y
        == scrollView.contentSize.height - scrollView.bounds.size.height)
    {
      /* Stop scrolling the main scroll view and start scrolling the
       * inner scroll view
       */
      self.innerScrollView.scrollEnabled = YES;
      self.mainScrollView.scrollEnabled = NO;
    }
    else
    {
      /* Start scrolling the main scroll view and stop scrolling the
       * inner scroll view
       */
      self.innerScrollView.scrollEnabled = NO;
      self.mainScrollView.scrollEnabled = YES;
    }
  }
  else if (scrollView == self.innerScrollView)
  {
    /* Handle instances when the inner scroll view is already at the top */
    if (self.innerScrollView.contentOffset.y == 0)
    {
      /* Stop scrolling the inner scroll view and start scrolling the
       * main scroll view
       */
      self.innerScrollView.scrollEnabled = NO;
      self.mainScrollView.scrollEnabled = YES;
    }
    else
    {
      /* Start scrolling the inner scroll view and stop scrolling the
       * main scroll view
       */
      self.innerScrollView.scrollEnabled = YES;
      self.mainScrollView.scrollEnabled = NO;
    }
  }
}

Please note that I haven't tested this, but the logic could be somewhat like this (either you set scrolling enabled or you disable user interaction, or something). Most probably this will not be enough of a solution as you would like, but I'm sure a common UIScrollViewDelegate is the solution to your problem.

Military answered 29/4, 2013 at 3:1 Comment(3)
I have tried this and it "kinda" works. The problem is that the scroll/swipe gesture is not transferred from one scroll view to the other if you do it as you proposed. You have to drag/swipe again to the the second scroll view scrolling.Brookbrooke
You can use the scrollView's panGestureRecognizer to fetch the velocity at which the user is panning, calculate both the animation duration and the content offset delta, and fake a pan on either the mainScrollView or the innerScrollView when the other has reached the top/bottom.Military
The solution I found for the problem @Brookbrooke was having is Replace self.mainScrollView.scrollEnabled = NO; to self.mainScrollView.scrollEnabled = YES We don't have to disable scrolling for mainScrollView as the focus for mainScrollView is handle automatically depends on the innerScrollView Scroll Position and Direction of Scrolling Try it out , it works for meTeledu
T
1

I given the example for one scrollview, samething you have to create onemore scrollview and add based on dynamic height and content size it will work.

// .h file

@property (nonatomic, strong) UIScrollView *scrlSearch;

// .m File // ViewDidLoad

scrlSearch = [[UIScrollView alloc] init];

// For first scroll screen height was ((total screen height / 100 )* 10% )
// For Second scroll screen height was ((total screen height / 100 )* 90% )
scrlSearch.frame = CGRectMake(0, 0, (YourScreenWidth), (YourScreenHeight));

// YourVIEW = add any view to scrollbar
[scrlSearch addSubview:YourVIEW];

CGSize contentSize = scrlSearch.frame.size;
// YourContentHeight = dynamic content or static content height
contentSize.height = YourContentHeight;

// set the ContentHeight for scrolling
[scrlSearch setContentSize:contentSize];
// add the scrollview into delegate
[scrlSearch setDelegate:self];
Transponder answered 27/4, 2013 at 9:47 Comment(2)
I'm sorry, but how is this even an answer?Rawlins
@Rawlins I did some examples using UIscrollview . its worked for me thats what i shared this code.Transponder
C
0

Use just one scrollView and set the contentInset/contentOffset for the search bar. Something along this line:

  UIEdgeInsets oldEdgeInset = [[self scrollView] contentInset];
  CGRect f = [[self searchBar] frame];
  UIEdgeInsets newEdgeInset = UIEdgeInsetsMake(CGRectGetMaxY(f), 0, 0, 0);
  CGPoint offset = [[self scrollView] contentOffset];
  offset.y += oldEdgeInset.top - newEdgeInset.top;
  [[self scrollView] setContentOffset:offset];
  [[self scrollView] setContentInset:newEdgeInset];
  [[self searchBar] setFrame:f];
Compulsion answered 20/4, 2013 at 12:17 Comment(1)
Unfortunately, in my case it's a little more complicated than this :( The inner scroll view is actually a horizontal pager which contains multiple table views. It's a self-contained view that I cannot extend easily. I'm looking for a solution more in the direction of an overlaying superview that will let me route events to the appropriate scroll views...Brookbrooke
B
0

I am not sure about the structure you have and want to implement..

I have made a test project find here

But the project will definitely help you to manage different scrollviews all together..

The application might not be perfect though, but will give you some idea to achieve the solution.

Hope it helps..

Cheers

Banuelos answered 24/4, 2013 at 8:41 Comment(0)
L
0

You should add inner scrollview on outer scrollview, but inner scrollview 'y' position should be content off set of outer scrollview like...

innerScrollView.frame = CGRectMake(10, outerScrollView.contentSize.height, 300, 440);

After this you can add any view on this innerScrollView, you can set contentOffset etc for innerScrollView.

Finally you should increase the contentSize for outer scrollview.

outerScrollView.contentSize = CGSizeMake(320, innerScrollView.frame.size.height+actualContentSizeOfOuterScroolView);

I think it helps for you!

Lamella answered 25/4, 2013 at 8:11 Comment(0)
O
0

Create a swipe gesture recognizer to your top-most view (above all the scroll views, on your view controller's view), and make it recognize UISwipeGestureRecognizerDirectionUp.

Then, you need to notify your controller whenever the outer scroll view scrolls. Once it's scrolled down, add the gesture recognizer. When it hits the top again (outerScrollView.contentOffset == (0,0)), remove the gesture recognizer.

The gesture should 'eat' all the swiping events while it's present, making your inner scroll views to not receive the touch event and therefore will not scroll

Oldworld answered 29/4, 2013 at 13:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.