Making two UIScrollViews follow each others scrolling

前端 未结 4 1650
名媛妹妹
名媛妹妹 2020-12-12 19:54

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 dow

相关标签:
4条回答
  • 2020-12-12 20:03

    Swift 5.2 // Xcode 12.1

    What has worked flawlessly for me was the following:

    1. Create a custom subclass of UIScrollView
    2. Conform to UIGestureRecognizer delegate
    3. Override the gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) GestureRecognizerDelegate method
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return true
        }
    
    1. Both collection views need to have the same superview. Add both gesture recognizers to your superview
    yourSuperview.addGestureRecognizer(scrollView1.panGestureRecognizer)
    yourSuperview.addGestureRecognizer(scrollView1)
    

    Hope this helps!

    0 讨论(0)
  • 2020-12-12 20:15

    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.

    0 讨论(0)
  • 2020-12-12 20:15

    Guys i know the question is answered, but decided to share with you here my approach for solving a similar issue that i had, because i believe it is a pretty clean solution. Basically i had to make three collection views scroll together, and what i did, is i made a custom UICollectionView subclass called SharedOffsetCollectionView, that when you set this class to a collection view in storyboards or you instantiate it from code directly, you can have all instances scroll the same.

    So with SharedOffsetCollectionView all collection view instances of this class in your app, will scroll the same always. In my opinion it is a clean solution because it requires adding zero logic in your view controllers, it is all contained in this external class, you just have to set the class of your collection view to be SharedOffsetCollectionView and you are done.

    The same approach could easily be transferred to UITableViews and UIScrollViews

    Hope that is helpful to some you. :)

    My solution is written in:

    Swift 5.2, XCode 11.4.1

    0 讨论(0)
  • 2020-12-12 20:16

    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)
        }
    
    0 讨论(0)
提交回复
热议问题