ITKeyword,专注技术干货聚合推荐

注册 | 登录

(十三) Thunder分布式RPC框架

nepxion

up vote 207 down vote favorite 50

How can I make a UIScrollView scroll to the bottom within my code? Or in a more generic way, to any point of a subview?

ios objective-c iphone swift uiscrollview share | improve this question edited Jan 29 '16 at 12:20 Bartłomiej Semańczyk 18.8k 16 97 155 asked Jun 4 '09 at 18:50 nico 3,981 6 19 28 add a comment  | 

22 Answers 22

active oldest votes up vote 491 down vote accepted

You can use the UIScrollView's setContentOffset:animated: function to scroll to any part of the content view. Here's some code that would scroll to the bottom, assuming your scrollView is self.scrollView:

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

Hope that helps!

share | improve this answer edited Aug 24 '13 at 4:27 Rod 17.9k 1 24 45 answered Jun 4 '09 at 19:58 Ben Gotow 11.6k 3 32 45      thanks! that helped. –  nico Jun 5 '09 at 15:31      works perfect! thanks –  Lior Frenkel Apr 15 '11 at 16:47 20   you probably want to the following to scroll to the bottom, instead of out of the scrollview: CGPoint bottomOffset = CGPointMake(0, [self.scrollView contentSize].height - self.scrollView.frame.size.height); –  Grantland Chew Nov 2 '11 at 0:22 3   Ben's solution just scrolled my content out of sight... –  mootymoots Dec 18 '11 at 10:15 44   Your are not taking insets into account. I think You should prefer this bottomOffset: CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom); –  Martin Dec 16 '14 at 10:24  |  show 9 more comments up vote 36 down vote

Simplest Solution:

[scrollview scrollRectToVisible:CGRectMake(scrollview.contentSize.width - 1,scrollview.contentSize.height - 1, 1, 1) animated:YES];
share | improve this answer answered Mar 7 '14 at 8:41 Hai Hw 689 10 20      works like charms.. thanks for sharing.. –  vinay chorpa Nov 3 '14 at 17:52      Thanks. I forgot I changed my scrollview to a UITableView and this code worked for a UITableView as well, FYI. –  LevinsonTechnologies Sep 16 '15 at 23:43 add a comment  |  up vote 26 down vote

Swift version of the accepted answer for easy copy pasting:

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height)
scrollView.setContentOffset(bottomOffset, animated: true)
share | improve this answer edited Oct 11 '15 at 16:04 answered Apr 2 '15 at 11:49 Esqarrouth 14.5k 8 84 96 7   Oh we all love bacon, pizza, and copy pasting. –  Josh Jun 9 '15 at 10:04      bottomOffset should be let and not var in your example. –  Hobbes the Tige Oct 11 '15 at 15:59      true, changed it. swift2 stuff :) –  Esqarrouth Oct 11 '15 at 16:04 2   you need to check if scrollView.contentSize.height - scrollView.bounds.size.height > 0 otherwise you will have wierd results –  iluvatar_GR May 25 '16 at 11:41 add a comment  |  up vote 18 down vote

Setting the content offset to the height of the content size is wrong: it scrolls the bottom of the content to the top of the scroll view, and thus out of sight.

The correct solution is to scroll the bottom of the content to the bottom of the scroll view, like this (sv is the 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];
}
share | improve this answer answered Jun 11 '11 at 17:32 matt 232k 32 365 511 1   Shouldn't it be if (sv.contentOffset.y + csz.height > bsz.height) {? –  i_am_jorf Nov 14 '11 at 18:30      @jeffamaphone - Thanks for asking. Actually at this point I'm not even sure what purpose the condition was supposed to serve! It's the setContentOffset: command that's important. –  matt Nov 14 '11 at 20:46      I presume its to avoid the setContentOffset call if it isn't going to do anything? –  i_am_jorf Nov 15 '11 at 16:30      @jeffamaphone - It used to be that after device rotation a scroll view could end with its content bottom higher than the bottom of the frame (because if we rotate a scroll view its content offset remains constant, but the scroll view height may have grown due to autoresizing). The condition says: If that happens, put the content bottom back down at the bottom of the frame. But it was silly of me to include the condition when I pasted in the code. The condition is correctly testing for what it was testing for, though. –  matt Nov 15 '11 at 17:21 add a comment  |  up vote 16 down vote

Just an enhancement to the existing answer.

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

It takes care of the bottom inset as well (in case you're using that to adjust your scroll view when the keyboard is visible)

share | improve this answer answered Sep 6 '13 at 11:33 vivek241 346 2 12      Thanks a lot, work like charm –  Suhaiyl Jan 20 '15 at 8:11      This should be the correct answer... not the other ones that'll break if they ever have a keyboard pop up. –  Ben Guild Jun 21 '16 at 7:38 add a comment  |  up vote 12 down vote

Scroll To Top

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

Scroll To Bottom

- CGPoint bottomOffset = CGPointMake(0, scrollView.contentSize.height - self.scrollView.bounds.size.height);
 - [scrollView setContentOffset:bottomOffset animated:YES];
share | improve this answer edited Oct 30 '12 at 15:05 Rakesh 2,733 1 14 38 answered Feb 17 '12 at 9:40 Waseem 131 2 5 add a comment  |  up vote 8 down vote

I also found another useful way of doing this in the case you are using a UITableview (which is a subclass of UIScrollView):

[(UITableView *)self.view scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
share | improve this answer edited Mar 11 '11 at 0:16 Jason Plank 2,122 4 24 38 answered Jun 5 '09 at 15:35 nico 3,981 6 19 28 add a comment  |  up vote 5 down vote

With an (optional) footerView and contentInset, the solution is:

CGPoint bottomOffset = CGPointMake(0, _tableView.contentSize.height - tableView.frame.size.height + _tableView.contentInset.bottom);
if (bottomOffset.y > 0) [_tableView setContentOffset: bottomOffset animated: YES];
share | improve this answer answered Jan 28 '12 at 19:59 Pieter 256 4 5      Man, you are god! –  hishamaus Dec 5 '14 at 0:02 add a comment  |  up vote 5 down vote

What if contentSize is lower than bounds?

For Swift it is:

scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
share | improve this answer answered Jan 16 '16 at 19:56 Bartłomiej Semańczyk 18.8k 16 97 155 add a comment  |  up vote 4 down vote

A Swift 2.2 solution, taking contentInset into account

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

This should be in an extension

extension UIScrollView {

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

Note that you may want to check if bottomOffset.y > 0 before scroll

share | improve this answer edited Jul 7 '16 at 12:19 answered Jul 7 '16 at 9:27 onmyway133 14.7k 10 91 137 add a comment  |  up vote 3 down vote

valdyr, hope this will help you:

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

if (bottomOffset.y > 0)
 [textView setContentOffset: bottomOffset animated: YES];
share | improve this answer edited Mar 11 '11 at 0:17 Jason Plank 2,122 4 24 38 answered Oct 29 '10 at 7:41 Misha 31 1 add a comment  |  up vote 2 down vote

Category to the rescue!

Add this to a shared utility header somewhere:

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

And then to that utility implementation:

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

Then Implement it wherever you like, for instance:

[[myWebView scrollView] scrollToBottomAnimated:YES];
share | improve this answer answered Mar 14 '13 at 19:18 BadPirate 19.3k 5 63 100      Always, always, always, always use categories! Perfect :) –  Joe Blow Mar 5 '14 at 8:27 add a comment  |  up vote 2 down vote

Using UIScrollView's setContentOffset:animated: function to scroll to the bottom in Swift.

let bottomOffset : CGPoint = CGPointMake(0, scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
share | improve this answer answered Jan 15 '16 at 15:15 Kim 41 1 add a comment  |  up vote 1 down vote

A good way to ensure the bottom of your content is visible is to use the formula:

contentOffsetY = MIN(0, contentHeight - boundsHeight)

This ensures the bottom edge of your content is always at or above the bottom edge of the view. The MIN(0, ...) is required because UITableView (and probably UIScrollView) ensures contentOffsetY >= 0 when the user tries to scroll by visibly snapping contentOffsetY = 0. This looks pretty weird to the user.

The code to implement this is:

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];
}
share | improve this answer answered Mar 27 '13 at 8:34 jjrscott 317 3 5 add a comment  |  up vote 1 down vote

If you don't need animation, this works:

[self.scrollView setContentOffset:CGPointMake(0, CGFLOAT_MAX) animated:NO];
share | improve this answer answered Jan 16 '14 at 15:44 Krys Jurgowski 1,793 10 21 add a comment  |  up vote 1 down vote

While Matt solution seems correct to me you need to take in account also the collection view inset if there is one that has been set-up.

The adapted code will be:

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];
}
share | improve this answer answered Jun 17 '14 at 14:09 tiguero 8,495 4 30 54 add a comment  |  up vote 1 down vote

A swifty implementation:

extension UIScrollView {
    func scrollToBottom(animated 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)
    }
}
share | improve this answer edited Jul 6 '16 at 15:11 answered Jul 6 '16 at 10:32 duan 2,603 1 16 33      @WayneFilkins just call it –  duan Oct 2 '16 at 2:32 add a comment  |  up vote 0 down vote

Didn't work for me, when I tried to use it in UITableViewController on self.tableView (iOS 4.1), after adding footerView. It scrolls out of the borders, showing black screen.

Alternative solution:

 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];
share | improve this answer edited Nov 8 '11 at 9:14 rptwsthi 7,808 8 44 78 answered Oct 27 '10 at 12:55 valdyr 134 1 7 add a comment  |  up vote 0 down vote
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];
share | improve this answer answered Jan 31 '14 at 8:23 8suhas 1,075 8 18 add a comment  |  up vote 0 down vote

I found that contentSize doesn't really reflect the actual size of the text, so when trying to scroll to the bottom, it will be a little bit off. The best way to determine the actual content size is actually to use the NSLayoutManager's usedRectForTextContainer: method:

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

To determine how much text actually is shown in the UITextView, you can calculate it by subtracting the text container insets from the frame height.

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

Then it becomes easy to scroll:

// 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;

Some numbers for reference on what the different sizes represent for an empty 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)
share | improve this answer answered Jun 26 '15 at 22:00 mikeho 3,297 20 36 add a comment  |  up vote 0 down vote

In swift:

   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)
    }
share | improve this answer answered Sep 24 '15 at 20:37 Ponja 371 3 12 add a comment  |  up vote 0 down vote

Solution to scroll to last item of a table View :

Swift 3 :

if self.items.count > 0 {
        self.tableView.scrollToRow(at:  IndexPath.init(row: self.items.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}
share | improve this answer answered Feb 28 at 20:18 MarionFlex 1 add a comment  | 

Your Answer

  draft saved draft discarded

Sign up or log in

Sign up using Google

Sign up using Facebook

Sign up using Email and Password

Post as a guest

Name Email

Post as a guest

Name Email discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged ios objective-c iphone swift uiscrollview or ask your own question.

Thunder(QQ 群 471164539)发布在淘宝代码基地 http://code.taobao.org/p/Thunder/ 基于Google Guava EventBus,实现事件驱动发布框架内部事件,解除耦合;发布外部事件,进行重试补偿,异常

相关阅读排行


用户评论

游客

相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。