How would I make two scroll views follow each others scrolling?
For instance, I have a scroll view (A) on the left of a screen, whose contents can scroll up and down, but not left and right. Scroll view B matches the up and down of A, but can also scroll left and right. Scroll view A is always on the screen.
----------------------------------------------------------- | | | | | | | | | | A | B | | | | | scrolls | | | up & down | scrolls all directions | | | | -----------------------------------------------------------
How would I make it so the the up and down scrolling (of either view) also makes the other view scroll in the same up-down direction? Or is there another method to do this?
Set the delegate of scroll view A to be your view controller... then have...
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGPoint offset = scrollViewB.contentOffset; offset.y = scrollViewA.contentOffset.y; [scrollViewB setContentOffset:offset]; }
If you want both to follow each other, then set delegate for both of them and use...
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { if([scrollView isEqual:scrollViewA]) { CGPoint offset = scrollViewB.contentOffset; offset.y = scrollViewA.contentOffset.y; [scrollViewB setContentOffset:offset]; } else { CGPoint offset = scrollViewA.contentOffset; offset.y = scrollViewB.contentOffset.y; [scrollViewA setContentOffset:offset]; } }
The above can be refactored to have a method which takes in two scrollviews and matches one to the other.
- (void)matchScrollView:(UIScrollView *)first toScrollView:(UIScrollView *)second { CGPoint offset = first.contentOffset; offset.y = second.contentOffset.y; [first setContentOffset:offset]; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if([scrollView isEqual:scrollViewA]) { [self matchScrollView:scrollViewB toScrollView:scrollViewA]; } else { [self matchScrollView:scrollViewA toScrollView:scrollViewB]; } }
Swift 3 Version:
func scrollViewDidScroll(_ scrollView: UIScrollView) { if scrollView == scrollViewA { self.synchronizeScrollView(scrollViewB, toScrollView: scrollViewA) } else if scrollView == scrollViewB { self.synchronizeScrollView(scrollViewA, toScrollView: scrollViewB) } } func synchronizeScrollView(_ scrollViewToScroll: UIScrollView, toScrollView scrolledView: UIScrollView) { var offset = scrollViewToScroll.contentOffset offset.y = scrolledView.contentOffset.y scrollViewToScroll.setContentOffset(offset, animated: false) }
I tried the Simon Lee's answer on iOS 11. It worked but not very well. The two scroll views was synchronized, but using his method, the scroll views would lost the inertia effect(when it continue to scroll after you release your finger) and the bouncing effect. I think it was due to the fact that setting the contentOffset
through setContentOffset(offset, animated: false)
method causes cyclic calls of the scrollViewDidScroll(_ scrollView: UIScrollView)
delegate's method(see this question)
Here is the solution that worked for me on iOS 11:
// implement UIScrollViewDelegate method func scrollViewDidScroll(_ scrollView: UIScrollView) { if scrollView == self.scrollViewA { self.syncScrollView(self.scrollViewB, toScrollView: self.scrollViewA) } else if scrollView == self.scrollViewB { self.syncScrollView(self.scrollViewA, toScrollView: scrollViewB) } } func syncScrollView(_ scrollViewToScroll: UIScrollView, toScrollView scrolledView: UIScrollView) { var scrollBounds = scrollViewToScroll.bounds scrollBounds.origin.y = scrolledView.contentOffset.y scrollViewToScroll.bounds = scrollBounds }
So instead of setting contentOffset
we are using bounds
property to sync the other scrollView with the one that was scrolled by the user. This way the delegate method scrollViewDidScroll(_ scrollView: UIScrollView)
is not called cyclically and the scrolling happens very smooth and with inertia and bouncing effects as with a single scroll view.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With