使两个 UIScrollView 跟随彼此滚动

发布于 2024-12-12 09:02:52 字数 810 浏览 0 评论 0原文

我如何使两个滚动视图跟随彼此滚动?

例如,我在屏幕左侧有一个滚动视图(A),其内容可以上下滚动,但不能左右滚动。滚动视图 B 与 A 的上下滚动一致,但也可以左右滚动。滚动视图 A 始终在屏幕上。

-----------------------------------------------------------
|             |                                           |
|             |                                           |
|             |                                           |
|     A       |                    B                      |
|             |                                           |
|    scrolls  |                                           |
|   up & down |              scrolls all directions       |
|             |                                           |
-----------------------------------------------------------

我该如何做到这一点,以便(任一视图的)上下滚动也使另一个视图以相同的上下方向滚动?或者还有其他方法可以做到这一点吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

小傻瓜 2024-12-19 09:02:52

将滚动视图 A 的委托设置为您的视图控制器...然后...

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  CGPoint offset = scrollViewB.contentOffset;
  offset.y = scrollViewA.contentOffset.y;
  [scrollViewB setContentOffset:offset];
}

如果您希望两者相互跟随,则为它们两个设置委托并使用...

- (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];
  }
}

上面可以重构为有一个方法它接受两个滚动视图并将其中一个与另一个相匹配。

- (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 版本:

    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)
    }

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)
    }
简单气质女生网名 2024-12-19 09:02:52

我在 iOS 11 上尝试了 Simon Lee 的答案。它有效,但不是很好。两个滚动视图是同步的,但是使用他的方法,滚动视图会失去惯性效果(松开手指后继续滚动)和弹跳效果。我认为这是因为通过 setContentOffset(offset,animated: false) 方法设置 contentOffset 会导致 scrollViewDidScroll(_scrollView: UIScrollView) 的循环调用) 委托方法(参见 这个问题

这是在 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
    }

所以不要设置 < code>contentOffset 我们使用 bounds 属性将另一个滚动视图与用户滚动的滚动视图同步。这样,委托方法 scrollViewDidScroll(_scrollView: UIScrollView) 就不会被循环调用,并且滚动会非常平滑地进行,并且与单个滚动视图一样具有惯性和弹跳效果。

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.

冷弦 2024-12-19 09:02:52

Swift 5.4 // Xcode 13.1

对我来说完美的工作如下:

  1. 创建 UIScrollView 的自定义子类
  2. 符合 UIGestureRecognizer 委托
  3. 覆盖 gestureRecognizer(_:shouldRecognizeSimultaneouslyWith :) GestureRecognizerDelegate 方法
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
  1. 两个集合视图需要具有相同的超级视图。将两个手势识别器添加到您的超级视图中
yourSuperview.addGestureRecognizer(scrollView1.panGestureRecognizer)
yourSuperview.addGestureRecognizer(scrollView2.panGestureRecognizer)

希望这有帮助!

Swift 5.4 // Xcode 13.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(scrollView2.panGestureRecognizer)

Hope this helps!

鹿港巷口少年归 2024-12-19 09:02:52

伙计们,我知道问题已经得到解答,但决定在这里与大家分享我解决类似问题的方法,因为我相信这是一个非常干净的解决方案。基本上我必须让三个集合视图一起滚动,我所做的是我做了一个 自定义 UICollectionView 子类名为 SharedOffsetCollectionView,当您将此类设置为故事板中的集合视图,或者直接从代码实例化它,您可以让所有实例以相同的方式滚动。

因此,使用 SharedOffsetCollectionView 时,应用程序中此类的所有集合视图实例将始终以相同的方式滚动。在我看来,这是一个干净的解决方案,因为它需要在视图控制器中添加零逻辑,它全部包含在这个外部类中,您只需将集合视图的类设置为 SharedOffsetCollectionView 并你完成了。

相同的方法可以轻松地转移到 UITableViewUIScrollViews

希望这对您有所帮助。 :)

我的解决方案是用以下语言编写的:

Swift 5.2,XCode 11.4.1

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

雪落纷纷 2024-12-19 09:02:52

最重要的答案对我来说不太有效,因为我遇到了 scrollViewDidScroll 的循环调用。发生这种情况是因为设置滚动视图的内容偏移量也会调用scrollViewDidScoll。我通过在它之间放置一个锁来解决这个问题,该锁是根据用户是否拖动滚动视图来设置的,因此通过设置内容偏移量不会发生同步:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    self.synchronizeScrollView(offSet: scrollView.contentOffset.y,
                               scrollViewAIsScrolling: scrollView == scrollViewA,
                               isScroller: scrollView.isDragging)
}

private enum Scroller {
    case page, time
}

private var scroller: Scroller? = nil

func synchronizeScrollView(offSet: CGFloat, scrollViewAIsScrolling: Bool,
                           isScroller: scrollView.isDragging) {
    
    let scrollViewToScroll = scrollViewAIsScrolling ? scrollViewB : scrollViewA
    var offset = scrollViewToScroll.contentOffset
    offset.y = offSet
    scrollViewToScroll.setContentOffset(offset, animated: false)
}

可以根据有多少个内容来重构此代码滚动视图的使用取决于它们的所有者。我不建议让一个控制器作为许多滚动视图的代表。我宁愿通过委托来解决它。

The answer above all did not quite work our for me, since I run into a cyclic call of scrollViewDidScroll. This happend, because setting the content offset of a scroll view also calls scrollViewDidScoll. I solved it by putting a lock between it, which is set based on if a scroll view is being dragged by the user or not, so the syncing won't happen by setting the content offset:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    self.synchronizeScrollView(offSet: scrollView.contentOffset.y,
                               scrollViewAIsScrolling: scrollView == scrollViewA,
                               isScroller: scrollView.isDragging)
}

private enum Scroller {
    case page, time
}

private var scroller: Scroller? = nil

func synchronizeScrollView(offSet: CGFloat, scrollViewAIsScrolling: Bool,
                           isScroller: scrollView.isDragging) {
    
    let scrollViewToScroll = scrollViewAIsScrolling ? scrollViewB : scrollViewA
    var offset = scrollViewToScroll.contentOffset
    offset.y = offSet
    scrollViewToScroll.setContentOffset(offset, animated: false)
}

This code can be refactored based on how many scroll views are used and based on who owns them. I won't recommend having one controller being the delegate of many scroll views. I would rather solve it with delegation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文