UIScrollView编程滚动到底部

340

如何在我的代码中使UIScrollView滚动到底部?或者更一般地说,滚动到子视图的任意点?

31个回答

699
您可以使用UIScrollView的setContentOffset:animated:函数来滚动到内容视图的任何部分。以下是一些代码,假设您的scrollView是self.scrollView,则会滚动到底部:
Objective-C:
CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];

Swift:

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.contentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)

25
你可能希望将以下代码滚动至底部,而不是滚出可滚动范围:CGPoint bottomOffset = CGPointMake(0, [self.scrollView contentSize].height - self.scrollView.frame.size.height); - Grantland Chew
75
您没有考虑到插图。我认为您应该使用这个bottomOffset: CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom); - Martin
1
这在横屏模式下不起作用!无法完全滚动到底部。 - Mo Farhand
6
如果 contentSize 小于 bounds,则它将无法正常工作。因此应该像这样:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)。请注意,我已尽力使翻译表达通俗易懂,同时保持原意不变。 - Bartłomiej Semańczyk
1
如果您正在使用集合视图,请勿忘记底部安全区域插入。 let bot = max(cv.contentSize.height - cv.bounds.height + cv.contentInset.bottom + cv.safeAreaInsets.bottom, 0) - Xavier L.
显示剩余4条评论

154

易于复制粘贴的已接受答案的 Swift 版本:

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height)
scrollView.setContentOffset(bottomOffset, animated: true)

4
你的示例中应该使用 let 而不是 var 来定义 bottomOffset。 - Hobbes the Tige
真的,改变了它。Swift2 的东西 :) - Esqarrouth
12
请检查scrollView.contentSize.height - scrollView.bounds.size.height是否大于0,否则可能会得到奇怪的结果。 - iluvatar_GR
3
当你拥有新的导航栏时,这个解决方案并不完美。 - Swift Rabbit
@Josh,仍在等待SO发现这件事的那一天。 - Alexandre G

55

最简单的解决方案:

[scrollview scrollRectToVisible:CGRectMake(scrollview.contentSize.width - 1,scrollview.contentSize.height - 1, 1, 1) animated:YES];

谢谢。我忘记了我把我的ScrollView改成了UITableView,这段代码也适用于UITableView,供您参考。 - LevinsonTechnologies
2
为什么不直接使用以下代码: [scrollview scrollRectToVisible:CGRectMake(0, scrollview.contentSize.height, 1, 1) animated:YES]; - Johannes

32

一种简洁的实现:

extension UIScrollView {
   func scrollToBottom(animated: Bool) {
     if self.contentSize.height < self.bounds.size.height { return }
     let bottomOffset = CGPoint(x: 0, y: self.contentSize.height - self.bounds.size.height)
     self.setContentOffset(bottomOffset, animated: animated)
  }
}

使用它:

yourScrollview.scrollToBottom(animated: true)

29

仅对现有答案进行了增强。

CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];

它也会处理底部插入(即使在键盘可见时调整滚动视图时使用该插入)。


这应该是正确的答案……而不是其他那些如果键盘弹出就会崩溃的答案。 - Ben Guild

19

将内容偏移设置为内容大小的高度是错误的:它会将内容底部滚动到滚动视图的顶部,因此不可见。

正确的解决方法是将内容底部滚动到滚动视图的底部,像这样(sv是UIScrollView):

CGSize csz = sv.contentSize;
CGSize bsz = sv.bounds.size;
if (sv.contentOffset.y + bsz.height > csz.height) {
    [sv setContentOffset:CGPointMake(sv.contentOffset.x, 
                                     csz.height - bsz.height) 
                animated:YES];
}

1
难道不应该是 if (sv.contentOffset.y + csz.height > bsz.height) { 吗? - i_am_jorf
@jeffamaphone - 感谢您的提问。实际上,我现在甚至不确定条件应该起到什么作用!重要的是setContentOffset:命令。 - matt
我猜这是为了避免在不需要时调用setContentOffset方法,对吗? - i_am_jorf
@jeffamaphone - 以前设备旋转后,滚动视图的内容底部可能会高于框架底部(因为如果我们旋转滚动视图,其内容偏移量保持不变,但由于自动调整大小,滚动视图的高度可能会增加)。该条件表示:如果发生这种情况,请将内容底部放回框架底部。但是当我粘贴代码时,我傻了,包含了这个条件。然而,该条件确实正确地测试了它所要测试的内容。 - matt

16

考虑到contentInset,这是一个Swift 2.2的解决方案。

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)

这应该放在一个扩展中

extension UIScrollView {

  func scrollToBottom() {
    let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
    setContentOffset(bottomOffset, animated: true)
  }
}

请注意,在滚动之前您可能想检查bottomOffset.y > 0


15

如果contentSizebounds小怎么办?

对于Swift,它是:

scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)

13

回到顶部

- CGPoint topOffset = CGPointMake(0, 0);
- [scrollView setContentOffset:topOffset animated:YES];

滚动到底部

- CGPoint bottomOffset = CGPointMake(0, scrollView.contentSize.height - self.scrollView.bounds.size.height);
 - [scrollView setContentOffset:bottomOffset animated:YES];

11

看起来这里的所有答案都没有考虑到安全区域。 自iOS 11开始,iPhone X引入了一个安全区域。这可能会影响scrollView的contentInset属性。

对于iOS 11及以上版本,为了正确地滚动到包括内容插图的底部,应该使用adjustedContentInset而不是contentInset。请检查以下代码:

  • Swift:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
  • Objective-C
CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.adjustedContentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];
  • Swift扩展(这将保留原始的contentOffset.x):
extension UIScrollView {
    func scrollsToBottom(animated: Bool) {
        let bottomOffset = CGPoint(x: contentOffset.x,
                                   y: contentSize.height - bounds.height + adjustedContentInset.bottom)
        setContentOffset(bottomOffset, animated: animated)
    }
}

参考资料:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接