UIScrollView以编程方式滚动到底部


Answers:


626

您可以使用UIScrollView的setContentOffset:animated:功能滚动到内容视图的任何部分。假设您的scrollView是,这是一些滚动到底部的代码self.scrollView

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

希望有帮助!


25
您可能希望以下内容滚动到底部,而不是滚动到底部:CGPoint bottomOffset = CGPointMake(0,[self.scrollView contentSize] .height-self.scrollView.frame.size.height);
Grantland Chew

70
您没有考虑到插图。我认为您应该更喜欢此bottomOffset:CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
马丁

1
这在横向模式下不起作用!不能完全滚动到底部
Mo Farhand

1
contentSize低于则无法正常工作bounds。因此,它应该是这样的:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
巴特洛梅耶Semańczyk

1
这可以处理底部插入,并且超出0:let bottomOffsetY = max(collectionView.contentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom, 0)
有意义的

136

易于接受的答案的Swift版本:

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

3
在您的示例中,bottomOffset应该设置为let,而不是var。
霍布斯巨虎

是的,改变了它。swift2的东西:)
Esqarrouth 2015年

9
你需要检查,如果scrollView.contentSize.height - scrollView.bounds.size.height> 0,否则,你将有奇怪的结果
iluvatar_GR

2
当您拥有新的导航栏时,此解决方案并不完美。
Swift Rabbit

@Josh,仍在等待SO发现这一天的时间
Alexandre G

53

最简单的解决方案:

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

谢谢。我忘记了将滚动视图更改为UITableView,并且此代码也适用于UITableView,仅供参考。
LevinsonTechnologies

1
为什么不这样:[scrollview scrollRectToVisible:CGRectMake(0,scrollview.contentSize.height,1,1)animation:YES];
约翰内斯,

29

只是对现有答案的增强。

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

它还会照顾底部的插图(以防您在可见键盘时使用它来调整滚动视图)


这应该是正确的答案...如果弹出键盘,其他答案将不会中断。
本·

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:是重要的命令。
马特

我想它可以避免setContentOffset调用,如果它什么都不做呢?
i_am_jorf

@jeffamaphone-过去,设备旋转后,滚动视图可能会以其内容底部高于框架底部的高度结束(因为如果我们旋转滚动视图,其内容偏移量将保持不变,但是滚动视图高度可能由于自动调整大小)。条件显示:如果发生这种情况,将内容底部放回框架底部。但是在粘贴代码时包含条件是我很愚蠢的。但是,该条件正在正确测试其测试内容。
马特

19

快速实施:

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)

15

考虑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在滚动之前


14

如果contentSize低于bounds呢?

对于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];

7

在使用UITableview(这是UIScrollView的子类)的情况下,我还发现了另一种有用的方法:

[(UITableView *)self.view scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

仅供参考,这仅是UITableView功能
OhadM,

7

使用UIScrollView的setContentOffset:animated:功能滚动到Swift的底部。

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

5

似乎这里的所有答案都没有考虑到安全区域。从iOS 11开始,iPhone X引入了安全区域。这可能会影响scrollView的contentInset

对于iOS 11及更高版本,要正确滚动到底部并包含内容插图。您应该使用adjustedContentInset而不是contentInset。检查此代码:

  • 迅速:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
  • 物镜
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)
    }
}

参考文献:


5

使用和(可选)footerViewcontentInset,解决方案是:

CGPoint bottomOffset = CGPointMake(0, _tableView.contentSize.height - tableView.frame.size.height + _tableView.contentInset.bottom);
if (bottomOffset.y > 0) [_tableView setContentOffset: bottomOffset animated: YES];

4

迅速:

您可以使用如下扩展名:

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

采用:

scrollView.scrollsToBottom(animated: true)

3

valdyr,希望这对您有帮助:

CGPoint bottomOffset = CGPointMake(0, [textView contentSize].height - textView.frame.size.height);

if (bottomOffset.y > 0)
 [textView setContentOffset: bottomOffset animated: YES];

2

分类抢救!

将此添加到共享公用程序标头中的某个位置:

@interface UIScrollView (ScrollToBottom)
- (void)scrollToBottomAnimated:(BOOL)animated;
@end

然后执行该实用程序:

@implementation UIScrollView(ScrollToBottom)
- (void)scrollToBottomAnimated:(BOOL)animated
{
     CGPoint bottomOffset = CGPointMake(0, self.contentSize.height - self.bounds.size.height);
     [self setContentOffset:bottomOffset animated:animated];
}
@end

然后在任意位置实现它,例如:

[[myWebView scrollView] scrollToBottomAnimated:YES];

永远,永远,永远,永远使用类别!完美:)
Fattie 2014年

2

对于水平ScrollView

如果喜欢我的用户具有Horizo​​ntal ScrollView,并且想要滚动到它的末尾(在我的情况下,它最右边),则需要更改已接受答案的某些部分:

物镜

CGPoint rightOffset = CGPointMake(self.scrollView.contentSize.width - self.scrollView.bounds.size.width + self.scrollView.contentInset.right, 0 );
[self.scrollView setContentOffset:rightOffset animated:YES];

迅速

let rightOffset: CGPoint = CGPoint(x: self.scrollView.contentSize.width - self.scrollView.bounds.size.width + self.scrollView.contentInset.right, y: 0)
self.scrollView.setContentOffset(rightOffset, animated: true)

2

如果您以某种方式更改了scrollView contentSize(例如,将某些内容添加到scrollView内的stackView中),则必须scrollView.layoutIfNeeded()在滚动之前调用它,否则它什么都不做。

例:

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

1

确保可见内容底部的一种好方法是使用以下公式:

contentOffsetY = MIN(0, contentHeight - boundsHeight)

这样可以确保内容的底边始终位于视图底边或上方。MIN(0, ...)之所以需要,是因为UITableView(并且有可能UIScrollView)确保contentOffsetY >= 0用户何时通过明显捕捉来尝试滚动contentOffsetY = 0。这对用户来说看起来很奇怪。

实现此目的的代码是:

UIScrollView scrollView = ...;
CGSize contentSize = scrollView.contentSize;
CGSize boundsSize = scrollView.bounds.size;
if (contentSize.height > boundsSize.height)
{
    CGPoint contentOffset = scrollView.contentOffset;
    contentOffset.y = contentSize.height - boundsSize.height;
    [scrollView setContentOffset:contentOffset animated:YES];
}

1

如果您不需要动画,可以使用以下方法:

[self.scrollView setContentOffset:CGPointMake(0, CGFLOAT_MAX) animated:NO];

1

Matt解决方案对我来说似乎是正确的,但如果已经设置了集合视图插图,则还需要考虑集合视图插图。

修改后的代码将是:

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

1

迅速:

   if self.mainScroll.contentSize.height > self.mainScroll.bounds.size.height {
        let bottomOffset = CGPointMake(0, self.mainScroll.contentSize.height - self.mainScroll.bounds.size.height);
        self.mainScroll.setContentOffset(bottomOffset, animated: true)
    }

1

滚动到表格视图最后一项的解决方案:

迅捷3:

if self.items.count > 0 {
        self.tableView.scrollToRow(at:  IndexPath.init(row: self.items.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}

0

添加后,当我尝试在UITableViewController上使用它时,对我不起作用self.tableView (iOS 4.1)footerView。它滚动出边界,显示黑屏。

替代解决方案:

 CGFloat height = self.tableView.contentSize.height; 

 [self.tableView setTableFooterView: myFooterView];
 [self.tableView reloadData];

 CGFloat delta = self.tableView.contentSize.height - height;
 CGPoint offset = [self.tableView contentOffset];
 offset.y += delta;

 [self.tableView setContentOffset: offset animated: YES];

0
CGFloat yOffset = scrollView.contentOffset.y;

CGFloat height = scrollView.frame.size.height;

CGFloat contentHeight = scrollView.contentSize.height;

CGFloat distance = (contentHeight  - height) - yOffset;

if(distance < 0)
{
    return ;
}

CGPoint offset = scrollView.contentOffset;

offset.y += distance;

[scrollView setContentOffset:offset animated:YES];

0

我发现这contentSize并不能真正反映文本的实际大小,因此当尝试滚动到底部时,会有点偏离。确定实际内容大小的最佳方法实际上是使用NSLayoutManagerusedRectForTextContainer:方法:

UITextView *textView;
CGSize textSize = [textView.layoutManager usedRectForTextContainer:textView.textContainer].size;

要确定实际显示了多少文本UITextView,您可以通过从框架高度中减去文本容器插图来进行计算。

UITextView *textView;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textViewHeight = textView.frame.size.height - textInsets.top - textInsets.bottom;

然后变得容易滚动:

// if you want scroll animation, use contentOffset
UITextView *textView;
textView.contentOffset = CGPointMake(textView.contentOffset.x, textSize - textViewHeight);

// if you don't want scroll animation
CGRect scrollBounds = textView.bounds;
scrollBounds.origin = CGPointMake(textView.contentOffset.x, textSize - textViewHeight);
textView.bounds = scrollBounds;

一些数字可供参考,以表示空的不同尺寸UITextView

textView.frame.size = (width=246, height=50)
textSize = (width=10, height=16.701999999999998)
textView.contentSize = (width=246, height=33)
textView.textContainerInset = (top=8, left=0, bottom=8, right=0)

0

扩展UIScrollView以添加scrollToBottom方法:

extension UIScrollView {
    func scrollToBottom(animated:Bool) {
        let offset = self.contentSize.height - self.visibleSize.height
        if offset > self.contentOffset.y {
            self.setContentOffset(CGPoint(x: 0, y: offset), animated: animated)
        }
    }
}

这对现有答案有什么作用?
灰胡子

-1

Xamarin.iOS版本的接受答案

var bottomOffset = new CGPoint (0,
     scrollView.ContentSize.Height - scrollView.Frame.Size.Height
     + scrollView.ContentInset.Bottom);

scrollView.SetContentOffset (bottomOffset, false);

-1

Xamarin.iOS版本UICollectionView的已接受答案,易于复制和粘贴

var bottomOffset = new CGPoint (0, CollectionView.ContentSize.Height - CollectionView.Frame.Size.Height + CollectionView.ContentInset.Bottom);          
CollectionView.SetContentOffset (bottomOffset, false);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.