Get UIScrollView to scroll to the top
Asked Answered
R

17

190

How do I make a UIScrollView scroll to the top?

Rotten answered 26/2, 2012 at 3:32 Comment(0)
P
331

UPDATE FOR iOS 7

[self.scrollView setContentOffset:
    CGPointMake(0, -self.scrollView.contentInset.top) animated:YES];

ORIGINAL

[self.scrollView setContentOffset:CGPointZero animated:YES];

or if you want to preserve the horizontal scroll position and just reset the vertical position:

[self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, 0)
    animated:YES];
Premeditation answered 26/2, 2012 at 3:40 Comment(5)
If you're doing this for iOS 7, you may have to take into account the UIScrollView contentInset, unfortunately. [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, -self.scrollView.contentInset.top) animated:YES]; does the job for meCarmon
I was able to scroll to the top, right below the navigation and status bar. [scrollView setContentOffset:CGPointMake(0, -scrollView.contentInset.top) animated:YES]; It works in iOS 6 & 7Troytroyer
This didn't work for me, probably because I'm doing something crazy with an inverted view in sprite kit. But if anyone else is as crazy as me - and your scrollview is at the bottom by default - this will scroll it to the top: [scrollView setContentOffset:CGPointMake(0, scrollView.contentSize.height-scrollView.frame.size.height) animated:YES];Barolet
Late-in-the-day comment: you should adjust for the content inset regardless of OS version. iOS 7 happens to use it for allowing scroll content below the status bar but it's always been exposed as a property, so always been potentially used by somebody.Lisp
For iOS 11+, Jakub Truhlář's answer below using adjustedContentInset was helpful, at least in my case.Serafina
S
76

Here is a Swift extension that makes it easy:

extension UIScrollView {
    func scrollToTop() {
        let desiredOffset = CGPoint(x: 0, y: -contentInset.top)
        setContentOffset(desiredOffset, animated: true)
   }
}

Usage:

myScrollView.scrollToTop()
Salvadorsalvadore answered 1/2, 2016 at 0:37 Comment(0)
M
62

For Swift 4

scrollView.setContentOffset(.zero, animated: true)
Muraida answered 12/2, 2018 at 10:55 Comment(1)
Definitely the best modern answer.Mule
G
54

iOS 11 and above

Try to play around with the new adjustedContentInset (It should even work with prefersLargeTitles, safe area etc.)

For example (scroll to the top):

var offset = CGPoint(
    x: -scrollView.contentInset.left, 
    y: -scrollView.contentInset.top)

if #available(iOS 11.0, *) {
    offset = CGPoint(
        x: -scrollView.adjustedContentInset.left, 
        y: -scrollView.adjustedContentInset.top)    
}

scrollView.setContentOffset(offset, animated: true)
Graber answered 6/10, 2017 at 23:35 Comment(0)
G
23

Use setContentOffset:animated:

[scrollView setContentOffset:CGPointZero animated:YES];
Glasshouse answered 26/2, 2012 at 3:40 Comment(0)
S
19

Answer for Swift 2.0/3.0/4.0 and iOS 7+:

let desiredOffset = CGPoint(x: 0, y: -self.scrollView.contentInset.top)
self.scrollView.setContentOffset(desiredOffset, animated: true)
Selfheal answered 8/1, 2016 at 22:27 Comment(0)
M
8

In iOS7 I had trouble getting a particular scrollview to go to the top, which worked in iOS6, and used this to set the scrollview to go to the top.

[self.myScroller scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
Miamiami answered 22/9, 2013 at 22:11 Comment(2)
I have the same issue in iOS7. Does anyone know how to enable this for a UITableView?Algonquin
It's funny how this works when animated is NO, but not when animated is YES. If YES, then it scrolls almost to the topdevgm16Erna
D
8

In SWIFT 5 Just set content Offset to zero

scrollView.setContentOffset(CGPoint.zero, animated: true)
Demaggio answered 2/6, 2020 at 9:4 Comment(0)
S
4

Swift 3.0.1 version of rob mayoff's answer :

self.scrollView.setContentOffset(
CGPoint(x: 0,y: -self.scrollView.contentInset.top),
animated: true)
Supine answered 1/11, 2016 at 16:52 Comment(0)
M
3

I think I have an answer that should be fully compatible with iOS 11 as well as prior versions (for vertical scrolling)

This takes into account the new adjustedContentInset and also accounts for the additional offset required when prefersLargeTitles is enabled on the navigationBar which appears to require an extra 52px offset on top of whatever the default is

This was a little tricky because the adjustedContentInset changes depending on the titleBar state (large title vs small title) so I needed to check and see what the titleBar height was and not apply the 52px offset if its already in the large state. Couldn't find any other method to check the state of the navigationBar so if anyone has a better option than seeing if the height is > 44.0 I'd like to hear it

func scrollToTop(_ scrollView: UIScrollView, animated: Bool = true) {
    if #available(iOS 11.0, *) {
        let expandedBar = (navigationController?.navigationBar.frame.height ?? 64.0 > 44.0)
        let largeTitles = (navigationController?.navigationBar.prefersLargeTitles) ?? false
        let offset: CGFloat = (largeTitles && !expandedBar) ? 52: 0
        scrollView.setContentOffset(CGPoint(x: 0, y: -(scrollView.adjustedContentInset.top + offset)), animated: animated)
    } else {
        scrollView.setContentOffset(CGPoint(x: 0, y: -scrollView.contentInset.top), animated: animated)
    }
}

Inspired by Jakub's solution

Mathieu answered 8/5, 2018 at 15:11 Comment(0)
S
3

It's very common when your navigation bar overlaps the small portion of the scrollView content and it looks like content starts not from the top. For fixing it I did 2 things:

  • Size Inspector - Scroll View - Content Insets --> Change from Automatic to Never.
  • Size Inspector - Constraints- "Align Top to" (Top Alignment Constraints)- Second item --> Change from Superview.Top to Safe Area.Top and the value(constant field) set to 0

Content insets - Never Align ScrolView.Top to Safe Area.Top

Scorch answered 25/9, 2018 at 11:5 Comment(0)
G
2

To fully replicate the status bar scrollToTop behavior we not only have to set the contentOffset but also want to make sure the scrollIndicators are displayed. Otherwise the user can quickly get lost.

The only public method to accomplish this is flashScrollIndicators. Unfortunately, calling it once after setting the contentOffset has no effect because it's reset immediately. I found it works when doing the flash each time in scrollViewDidScroll:.

// define arbitrary tag number in a global constants or in the .pch file
#define SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG 19291

- (void)scrollContentToTop {
    [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, -self.scrollView.contentInset.top) animated:YES];

    self.scrollView.tag = SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.scrollView.tag = 0;
    });
}

In your UIScrollViewDelegate (or UITable/UICollectionViewDelegate) implement this:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView.tag == SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG) {
        [scrollView flashScrollIndicators];
    }
}

The hide delay is a bit shorter compared to the status bar scrollToTop behavior but it still looks nice.

Note that I'm abusing the view tag to communicate the "isScrollingToTop" state because I need this across view controllers. If you're using tags for something else you might want to replace this with an iVar or a property.

Geminate answered 20/5, 2014 at 18:36 Comment(2)
I haven't tested, but it should throw this on the next runloop if you just use DISPATCH_TIME_NOW instead. Thanks for doing this work, not sure I need it yet, but good to know.Alvira
It's a while ago and I don't remember exactly but I believe this didn't work. Deferring to the next runloop is usually the first thing I try in such cases.Geminate
M
2

In modern iOS, set the the scroll view's content offset back to its top left adjustedContentInset:

let point = CGPoint(x: -scrollView.adjustedContentInset.left,
                    y: -scrollView.adjustedContentInset.top)
    
scrollView.setContentOffset(point, animated: true)
Melaniemelanin answered 19/10, 2021 at 21:26 Comment(0)
W
2

iOS 16

For table and collection views, the following always works for me:

let top = CGRect(x: 0, y: 0, width: 1, height: 1)
tableView.scrollRectToVisible(top, animated: true)
collectionView.scrollRectToVisible(top, animated: true)

For scroll views:

let top = CGPoint(x: 0, y: -adjustedContentInset.top)
scrollView.setContentOffset(top, animated: animated)

adjustedContentInset returns the insets applied by the safe area (if any) and any custom insets applied after instantiation. If either safe or custom insets are applied, the content inset of the scroll view when it's at its top will be negative, not zero, which is why this property should be used.

Windjammer answered 22/2, 2023 at 18:43 Comment(0)
E
0

Scroll to top for UITableViewController, UICollectionViewController or any UIViewController having UIScrollView

extension UIViewController {

  func scrollToTop(animated: Bool) {
    if let tv = self as? UITableViewController {
        tv.tableView.setContentOffset(CGPoint.zero, animated: animated)
    } else if let cv = self as? UICollectionViewController{
        cv.collectionView?.setContentOffset(CGPoint.zero, animated: animated)
    } else {
        for v in view.subviews {
            if let sv = v as? UIScrollView {
                sv.setContentOffset(CGPoint.zero, animated: animated)
            }
        }
    }
  }
}
Eskil answered 6/10, 2017 at 12:3 Comment(0)
V
-3

I tried all the ways. But nothing worked for me. Finally I did like this.

I added self.view .addSubview(self.scroll) line of code in the viewDidLoad. After started setting up frame for scroll view and added components to scroll view.

It worked for me.

Make sure you added self.view .addSubview(self.scroll) line in the beginning. then you can add UI elements.

Vinegary answered 11/3, 2016 at 8:3 Comment(2)
Please delete this. While it surely made sense at the time, your subview hierarchy issues aren't relevant to this question.Alvira
@Cristik if your scroll view isn't in the view hierarchy, or your computer is switched off, of course none of this will work. But the OP asked something pretty narrowAlvira
S
-3

iOS 2.0+ Mac Catalyst 13.0+

You can try: scrollView.scrollsToTop = true

You can refer it from documentation of developer.apple.com

Sible answered 2/12, 2020 at 4:25 Comment(2)
Hi and welcome to Stack Overflow! Please take the tour. Thanks for contributing an answer but can you also add an explanation on how it solves the problem?Authentic
Doing this will only enable/disable the scroll-to-top gesture, which is not what the question is about.Overline

© 2022 - 2024 — McMap. All rights reserved.