Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling touches for nested UIScrollViews scrolling in the same direction

I have two nested UIScrollViews, both scrolling in the vertical direction. I need the outer scrollview to scroll to it's max range first before allowing the inner scrollview to scroll. The inner scrollview should not be scrollable until the outer scrollview has reached it's max range. Here's an illustration: Nested Scrollviews Diagram

In the left diagram, a vertical drag inside of Scrollview B should move Scrollview A and Scrollview B should not be scrollable (but it still needs to be able to receive touches/taps). Once Scrollview A reaches it's max range (when Scrollview B gets to the top of the screen), then Scrollview B should scroll. This needs to work in one continuous motion.

I've attempted to toggle ScrollView B's scrollEnabled from ScrollView A's scrollViewDidScroll: delegate method, but this doesn't appear to be a viable solution because it doesn't work in one continuous motion (eg: The user needs to release and touch again after Scrollview B reaches the top of the screen).

What's the best way to implement this such that is works in one continuous motion?

like image 734
user2393462435 Avatar asked Dec 12 '14 19:12

user2393462435


People also ask

What is nested scrolling?

NestedScrollView is just like ScrollView, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. It is enabled by default. NestedScrollView is used when there is a need for a scrolling view inside another scrolling view.

What is nested scroll view in flutter?

A scrolling view inside of which can be nested other scrolling views, with their scroll positions being intrinsically linked.


1 Answers

I solved the problem in the following way. I am not really happy with it since it looks to me much too complicated, but it works (please note that the code below is a simplified, untested version of my code, which is due to a different UI more complicated):

I have 3 properties that control scrolling:

@property (nonatomic, assign) CGFloat currentPanY; @property (nonatomic, assign) BOOL    scrollA; @property (nonatomic, assign) BOOL    scrollB; 

2-step scrolling:

Disable scrolling for B, and enable scrolling for A.
This allows to scroll A .

When A reaches its max position, disable scrolling for A, and enable scrolling for B:

-(void)scrollViewDidScroll: (UIScrollView *)scrollView {     if (scrollView.contentOffset.y >= self.maxScrollUpOffset) {         [scrollView setContentOffset:CGPointMake(0, self.maxScrollUpOffset) animated:NO];                 self.scrollviewA.scrollEnabled = NO;         self.scrollviewB.scrollEnabled = YES;         self.scrollB = YES;     } } 

This gives the following effect:
When A is scrolled upwards it will stop scrolling when its max size is reached. However B will not start scrolling, since the pan gesture recognizer of A does not forward its actions to the pan gesture recognizer of B. Thus one has to lift the finger and to start a 2nd scrolling. Then, B will scroll. This gives the 2-step scrolling.

Continuous scrolling:

For continuous scrolling, B must scroll while the finger that started scrolling of A continues moving upwards. To detect this, I added a further pan gesture recognizer tho A, and enabled it to detect gestures simultaneously with the built-in gesture recognizers of A and B:

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

In the action of this additional pan gesture recognizer, I compute the distance the finger has moved upwards after the scrolling limit of A has been reached. By this distance, B is then scrolled programmatically:

- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)recognizer {     if (recognizer.state != UIGestureRecognizerStateChanged) {         self.currentPanY = 0;         self.scrollB = NO;         self.scrollA = NO;     } else {         CGPoint currentTranslation = [recognizer translationInView:self.scrollviewA];         CGFloat currentYup = currentTranslation.y;          if (self.scrollA || self.scrollB) {             if (self.currentPanY == 0) {                 self.currentPanY = currentYup;             }              CGFloat additionalYup = self.currentPanY - currentYup;             if (self.scrollA) {                 CGFloat offset = self.scrollviewA.scrollUpOffset + additionalYup;                 if (offset >= 0) {                     self.scrollviewA.contentOffset = CGPointMake(0, offset);                 } else {                     self.scrollviewA.contentOffset = CGPointZero;                 }             } else if (self.scrollB){                 self.scrollviewB.contentOffset = CGPointMake(0, additionalYup);             }         }     } }   

There is still a disadvantage:
If you start scrolling, lift the finger, and let the scrollView decelerate, it will behave like the 2-stage scrolling, since the additional pan gesture recognizer won’t recognize any pan gesture.

like image 53
Reinhard Männer Avatar answered Sep 20 '22 20:09

Reinhard Männer