Smooth Transition Between 2 Scrollviews

UIScrollView within a UIScrollView, how to keep a smooth transition when scrolling between the two

Yeah. Ive got a nifty trick for you.

Instead of having your red outlined view a scrollview make it a normal UIView that fills the screen. In that view lay out your scroll view (table view) and image view as they are in your illustration.

Place a scrollview that fills the bounds of the root view (i.e. also fills the screen) above all the other scrollview and image views. Set the content size of this view to be the total content height of all the views you want to scroll through. In otherwords there is an invisible scrollview sitting on top of all your other views and its content size height is inner scrollview (tableview) content size height + image view size height.

The heierarchy should look like this:

Sample Image

Then this scrollview on top that you have made with the really tall content size make its delegate be your view controller. Implement scrollViewDidScroll and we'll work some magic.

Scrollviews basically scroll by adjusting the bounds origin with funky formulas for momentum and stuff. So in our scrollviewDidScroll method we will simply adjust the bounds of the underlying views:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{

//the scroll view underneath (in the container view) will have a max content offset equal to the content height
//but minus the bounds height
CGFloat maxYOffsetForUnderScrollView = self.underScrollView.contentSize.height - self.underScrollView.bounds.size.height;

CGRect scrolledBoundsForContainerView = self.view.bounds;

if (scrollView.contentOffset.y <= maxYOffsetForUnderScrollView) {
//in this scenario we are still within the content for the underScrollView
//so we make sure the container view is scrolled to the top and set the offset for the contained scrollview
self.containerView.bounds = scrolledBoundsForContainerView;
self.underScrollview.contentOffset = scrollView.contentOffset;
return;
}

//in this scenario we have scrolled throug the entirety of the contained scrollview
//set its offset to the max and change the bounds of the container view to scroll everything else.
self.underScrollView.contentOffset = CGPointMake(0, maxYOffsetForUnderScrollView);

scrolledBoundsForContainerView.origin.y = scrollView.contentOffset.y - maxYOffsetForUnderScrollView;
self.containerView.bounds = scrolledBoundsForContainerView;
}

You will find that as scrollViewDidScroll is called every frame of animation that this faux scrolling of the contained views looks really natural.

But wait! I hear you say. That scroll view on top now intercepts ALL touches, and the views underneath it need to be touched as well. I have an interesting solution for that as well.

Set the scrollview on top to be off screen somewhere (i.e. set its frame off screen, but still the same size.) and then in your viewDidLoad method you add the scrollview's panGestureRecogniser to the main view. This will mean that you get all the iOS natural scrolling momentum and stuff without actually having the view on the screen. The contained scroll view will now probably go juddery as its pan gesture recognizer will get called as well (they work differently to UIEvent handling) so you will need to remove it.

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

[self.view addGestureRecognizer:self.scrollview.panGestureRecognizer];

[self.underScrollview removeGestureRecognizer:self.underScrollView.panGestureRecognizer];

//further code to set up content sizes and stuff
}

I had fun making this so heres a link to the sample project on github:
https://github.com/joelparsons/multipleScrollers

EDIT:

To show the scrollbar for the top scrollview when its off the screen no matter where you put it you can set the scrollIndicatorInsets to an inset created like this:

CGPoint scrollviewOrigin = self.scrollview.frame.origin;
self.scrollview.scrollIndicatorInsets = UIEdgeInsetsMake(-scrollviewOrigin.y,0,scrollviewOrigin.y,scrollviewOrigin.x);

*caveat that the scrollview still has to be the right height but I'm sure you get the idea.

And then to make the bar draw outside the scrollview's visible bounds you have to turn off clips to bounds

self.scrollview.clipsToBounds = NO;

Two simultaneous scroll views behaving weirdly

I finally discovered the answer.

Basically, contentOffset = 0 is a magic number: at this value, the scroll view needs to pick up its velocity from 0 even if the gesture already has a velocity. Therefore, simply set the default offset to something like 1 will do:

private func childScrollViewDidScroll(_ scrollView: UIScrollView) {
if self.childScrollViewLocked {
scrollView.contentOffset.y = 1 // <- set it to 1 instead
} else if scrollView.contentOffset.y <= 1 {
self.childScrollViewLocked = true
self.parentScrollViewLocked = false
}
}

For perfectionists, set a corresponding content inset can offset this artificial offset (whelp!).

Now the two scroll views can hand off scrolling smoothly.

Smooth move of the contentoffset UIScrollView Swift

You can use UIView.animations

  func goToPoint() {
dispatch_async(dispatch_get_main_queue()) {
UIView.animateWithDuration(2, delay: 0, options: UIViewAnimationOptions.CurveLinear, animations: {
self.scrollView.contentOffset.x = 200
}, completion: nil)
}
}

Swift version

DispatchQueue.main.async {
UIView.animate(withDuration: 0.2, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.myScrollView.contentOffset.x = CGFloat(startingPointForView)
}, completion: nil)
}

Swift 4

DispatchQueue.main.async {
UIView.animate(withDuration: 1, delay: 0, options: UIView.AnimationOptions.curveLinear, animations: {
self.scrollView.contentOffset.x = 200
}, completion: nil)
}

Make transition between two Menus with UIScrollView

Are you avoiding UIPageViewController because of the complexity? Because if you are there is a much easier way to accomplish the same gesture controlled transition using UIScrollView's pagingEnabled property

Based on what I see in the screenshots...

var size                                      = CGSize()
size.height = self.view.frame.height
size.width = self.view.frame.width * 2

let scrollView = UIScrollView()
scrollView.frame = self.view.frame
scrollView.contentSize = size
scrollView.pagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false

nav.center.y = scrollView.frame.height / 2
nav.center.x = scrollView.frame.width / 2
nav2.center.y = scrollView.frame.height / 2
nav2.center.x = nav.center.x + scrollView.frame.width

scrollView.addSubview(nav1)
scrollView.addSubview(nav2)

self.view.addSubview(scrollView)


Related Topics



Leave a reply



Submit