Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making two UIScrollViews follow each others scrolling

Tags:

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?

like image 473
cannyboy Avatar asked Oct 27 '11 15:10

cannyboy


2 Answers

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)     } 
like image 162
Simon Lee Avatar answered Oct 07 '22 14:10

Simon Lee


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.

like image 20
Dmitry Klochkov Avatar answered Oct 07 '22 13:10

Dmitry Klochkov