ChatGPT解决这个技术问题 Extra ChatGPT

UITableView - scroll to the top

In my table view I have to scroll to the top. But I cannot guarantee that the first object is going to be section 0, row 0. May be that my table view will start from section number 5.

So I get an exception, when I call:

[mainTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];

Is there another way to scroll to the top of table view?


c
catlan

UITableView is a subclass of UIScrollView, so you can also use:

[mainTableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];

Or

[mainTableView setContentOffset:CGPointZero animated:YES];

And in Swift:

mainTableView.setContentOffset(CGPointZero, animated:true)

And in Swift 3 & above:

mainTableView.setContentOffset(.zero, animated: true)

I first tried this with CGRectZero, which is equivalent to CGRectMake(0, 0, 0, 0). This does not work, but oddly the above does. I guess it needs a positive width and height. Thank you.
Note, you will want animated:NO if you run this in scrollToRowAtIndexPath: so as to make the table start in the correct position. Hope it helps!
@hasan83 CGRectMake(0, 0, 0, 0) or CGRectZero isn't a visible rect. I would also say that [mainTableView setContentOffset:CGPointZero animated:YES]; is the prettier expression.
anyone noticed that in iOS11 this all is broken and not scrolling correctly in some cases?
@PeterLapisu self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: UITableViewScrollPosition.top, animated: true) seems to work in iOS 11
r
rmaddy

Note: This answer isn't valid for iOS 11 and later.

I prefer

[mainTableView setContentOffset:CGPointZero animated:YES];

If you have a top inset on your table view, you have to subtract it:

[mainTableView setContentOffset:CGPointMake(0.0f, -mainTableView.contentInset.top) animated:YES];

Tomato, tomato. Hmm... That doesn't come across very clearly in writing.
This is the best way to use when you have table header or footer views and want them to be included too.
This is indeed a clear code, but it does not work when your tableView has non-zero contentInset from the top. For example: tableView.contentInset = UIEdgeInsetsMake(5.0f, 0.0f, 250.0f, 0.0f);. If that is the case, in your code the tableView scrolls to (0.0f, 5.0f).
The solution to my previous comment: [tableView setContentOffset:CGPointMake(0.0f, -tableView.contentInset.top) animated:YES];
On iOS 11 you should consider using -scrollView.adjustedContentInset.top instead.
A
Adrian

Possible Actions:

1

func scrollToFirstRow() {
    let indexPath = NSIndexPath(forRow: 0, inSection: 0)
    self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Top, animated: true)
}

2

func scrollToLastRow() {
    let indexPath = NSIndexPath(forRow: objects.count - 1, inSection: 0)
    self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
}

3

func scrollToSelectedRow() {
    let selectedRows = self.tableView.indexPathsForSelectedRows
    if let selectedRow = selectedRows?[0] as? NSIndexPath {
        self.tableView.scrollToRowAtIndexPath(selectedRow, atScrollPosition: .Middle, animated: true)
    }
}

4

func scrollToHeader() {
    self.tableView.scrollRectToVisible(CGRect(x: 0, y: 0, width: 1, height: 1), animated: true)
}

5

func scrollToTop(){
    self.tableView.setContentOffset(CGPointMake(0,  UIApplication.sharedApplication().statusBarFrame.height ), animated: true)
}

Disable Scroll To Top:

func disableScrollsToTopPropertyOnAllSubviewsOf(view: UIView) {
    for subview in view.subviews {
        if let scrollView = subview as? UIScrollView {
            (scrollView as UIScrollView).scrollsToTop = false
        }
        self.disableScrollsToTopPropertyOnAllSubviewsOf(subview as UIView)
    }
}

Modify and use it as per requirement.

Swift 4

  func scrollToFirstRow() {
    let indexPath = IndexPath(row: 0, section: 0)
    self.tableView.scrollToRow(at: indexPath, at: .top, animated: true)
  }

Thats the solution, perfect - scrollToFirstRow
Excellent summary. When you have section headers you'll have to use option4 scrollToHeader.
Is there a way to scroll up to a searchbar that is above the Tableview?
K
Kof

It's better to not use NSIndexPath (empty table), nor assume that top point is CGPointZero (content insets), that's what I use -

[tableView setContentOffset:CGPointMake(0.0f, -tableView.contentInset.top) animated:YES];

Hope this helps.


No longer valid as of iOS 11.
A
Alessandro Ornano

Swift 4:

This works very well:

//self.tableView.reloadData() if you want to use this line remember to put it before 
let indexPath = IndexPath(row: 0, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .top, animated: true)

Not if you have a tableHeaderView in your tableView and it's inline (scrolls with the content)
I have both a plain and a UITableViewStyleGrouped (headers scrolls with the contents) and this code works. Be sure you are in the main thread and you launch this code after the view appear (viewDidAppear). If you still have problems try to put the code inside this: DispatchQueue.main.asyncAfter(deadline: .now()+0.1, execute: { // the code }
Thank you. It will get you to the first cell. However, it doesn't show the table view header.
This will fail if the tableView is empty for some reason (no cell in 0 section at 0 row)
T
Thanh Pham

On iOS 11, use adjustedContentInset to correctly scroll to top for both cases when the in-call status bar is visible or not.

if (@available(iOS 11.0, *)) {
    [tableView setContentOffset:CGPointMake(0, -tableView.adjustedContentInset.top) animated:YES];
} else {
    [tableView setContentOffset:CGPointMake(0, -tableView.contentInset.top) animated:YES];
}

Swift:

if #available(iOS 11.0, *) {
    tableView.setContentOffset(CGPoint(x: 0, y: -tableView.adjustedContentInset.top), animated: true)
} else {
    tableView.setContentOffset(CGPoint(x: 0, y: -tableView.contentInset.top), animated: true)
}

It may not work sometimes unless you enclose that code inside tableView.beginUpdates() and tableView.endUpdates(). After this my scroll always works.
S
ScottyBlades

DONT USE

 tableView.setContentOffset(.zero, animated: true)

It can sometimes set the offset improperly. For example, in my case, the cell was actually slightly above the view with safe area insets. Not good.

INSTEAD USE

 tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)

Perfect Solution.
Neither of these solutions are valid. The first doesn't work under iOS 11 or later. The second doesn't work if the table view has a table header or the first section has a section header unless you really do want the first row at the top and don't care about showing any headers.
@rmaddy what's the best solution?
M
MobileMon

I've encountered an issue calling trying some of the methods on an empty tableView. Here's another option for Swift 4 that handles empty tableviews.

extension UITableView {
  func hasRowAtIndexPath(indexPath: IndexPath) -> Bool {
    return indexPath.section < self.numberOfSections && indexPath.row < self.numberOfRows(inSection: indexPath.section)
  }

  func scrollToTop(animated: Bool) {
    let indexPath = IndexPath(row: 0, section: 0)
    if self.hasRowAtIndexPath(indexPath: indexPath) {
      self.scrollToRow(at: indexPath, at: .top, animated: animated)
    }
  }
}

Usage:

// from yourViewController or yourTableViewController
tableView.scrollToTop(animated: true)//or false

This doesn't account for any table header or section header.
B
Bhavin Bhadani

For tables that have a contentInset, setting the content offset to CGPointZero will not work. It'll scroll to the content top vs. scrolling to the table top.

Taking content inset into account produces this instead:

[tableView setContentOffset:CGPointMake(0, -tableView.contentInset.top) animated:NO];

Thank you, I kept setting it to CGPointZero and could not understand why this was happening.
No longer valid as of iOS 11.
d
dreamzor

This code let's you scroll a specific section to top

CGRect cellRect = [tableinstance rectForSection:section];
CGPoint origin = [tableinstacne convertPoint:cellRect.origin 
                                    fromView:<tableistance>];
[tableinstance setContentOffset:CGPointMake(0, origin.y)];

This was the simplest code and it worked OK. I actually putted CGPointMake(0.0f, 0.0f). Cheers!
This code is very useful to make the section scroll top
f
fake girlfriends

Swift 5, iOS 13

I know this question already has a lot of answers but from my experience this method always works:

let last = IndexPath(row: someArray.count - 1, section: 0)
tableView.scrollToRow(at: last, at: .bottom, animated: true)

And this is especially true if you're working with animations (like keyboard) or certain async tasks—the other answers will often scroll to the almost bottom. If for some reason this doesn't get you all the way to the bottom, it's almost certainly because of a competing animation so the workaround is to dispatch this animation to the end of the main queue:

DispatchQueue.main.async {
    let last = IndexPath(row: self.someArray.count - 1, section: 0)
    self.tableView.scrollToRow(at: last, at: .bottom, animated: true)
}

This may seem redundant since you're already on the main queue but it's not because it serializes the animations.


You also need to check if there is a section
This works as long as you don't have zero rows in your tableView. Otherwise, I'd recommend using @catlan's answer
@craft if you have zero rows in your table then you shouldn't be attempting to scroll to the bottom anyway because it's already there.
@bsod Thanks for the reply. I actually do have a case where it's relevant. I'm scrolling back to the top with section headers but no rows. Also, I think the question is about top not bottom
E
Esqarrouth

Swift:

tableView.setContentOffset(CGPointZero, animated: true)

No longer valid as of iOS 11.
K
Kerim Khasbulatov

Swift 3

tableView.setContentOffset(CGPoint.zero, animated: true)

if tableView.setContentOffset don't work.

Use:

tableView.beginUpdates()
tableView.setContentOffset(CGPoint.zero, animated: true)
tableView.endUpdates()

This isn't valid for iOS 11 or later.
Kudos for mentioning beginUpdates() and endUpdates()
b
budiDino

Since my tableView is full of all kinds of insets, this was the only thing that worked well:

Swift 3

if tableView.numberOfSections > 0 && tableView.numberOfRows(inSection: 0) > 0 {
  tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
}

Swift 2

if tableView.numberOfSections > 0 && tableView.numberOfRowsInSection(0) > 0 {
  tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: .Top, animated: true)
}

this was more useful to me
great answer, appreciate it
o
ocwang

Adding on to what's already been said, you can create a extension (Swift) or category (Objective C) to make this easier in the future:

Swift:

extension UITableView {
    func scrollToTop(animated: Bool) {
        setContentOffset(CGPointZero, animated: animated)
    }
}

Any time you want to scroll any given tableView to the top you can call the following code:

tableView.scrollToTop(animated: true)

No longer valid as of iOS 11.
j
jacob

I prefer the following, as it takes into account an inset. If there is no inset, it will still scroll to the top as the inset will be 0.

tableView.setContentOffset(CGPoint(x: 0, y: -tableView.contentInset.top), animated: true)

No longer valid as of iOS 11.
j
jmcastel

Swift :

if you don't have tableView header :

tableView.setContentOffset(CGPointMake(0,  UIApplication.sharedApplication().statusBarFrame.height ), animated: true)

if so :

tableView.setContentOffset(CGPointMake(0, -tableViewheader.frame.height   + UIApplication.sharedApplication().statusBarFrame.height ), animated: true)

H
Hans Brende

Here's what I use to work correctly on iOS 11:

extension UIScrollView {
    func scrollToTop(animated: Bool) {
        var offset = contentOffset
        if #available(iOS 11, *) {
            offset.y = -adjustedContentInset.top
        } else {
            offset.y = -contentInset.top
        }
        setContentOffset(offset, animated: animated)
    }
}

d
dengST30

In Swift 5 , Thanks @Adrian's answer a lot

extension UITableView{

    func hasRowAtIndexPath(indexPath: IndexPath) -> Bool {
        return indexPath.section < numberOfSections && indexPath.row < numberOfRows(inSection: indexPath.section)
    }

    func scrollToTop(_ animated: Bool = false) {
        let indexPath = IndexPath(row: 0, section: 0)
        if hasRowAtIndexPath(indexPath: indexPath) {
            scrollToRow(at: indexPath, at: .top, animated: animated)
        }
    }

}

Usage:

tableView.scrollToTop()

1. This is also valid in Swift 4. 2. This doesn't work if there is a table header or a section header.
G
Gulz

using contentOffset is not the right way. this would be better as it is table view's natural way

tableView.scrollToRow(at: NSIndexPath.init(row: 0, section: 0) as IndexPath, at: .top, animated: true)

This won't work if there is a table header or a section header.
j
jimmyruby

This was the only code snippet that worked for me

Swift 4:

    tableView.scrollRectToVisible(CGRect(x: 0, y: 0, width: 1, height: 1), animated: true)
    tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
    tableView.setContentOffset(CGPoint(x: 0, y: -70), animated: true)

P.S. 70 is the height of my header and table view cell


This is far from the correct way to scroll to the top. It makes no sense to use three separate lines just to set the content offset. Only the last line is needed but hardcoding a specific offset is not the correct solution.
S
Sayali Shinde
func scrollToTop() {
        NSIndexPath *topItem = [NSIndexPath indexPathForItem:0 inSection:0];
        [tableView scrollToRowAtIndexPath:topItem atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

call this function wherever you want UITableView scroll to top


S
Shawn

Swift 4 via extension, handles empty table view:

extension UITableView {
    func scrollToTop(animated: Bool) {
        self.setContentOffset(CGPoint.zero, animated: animated);
    }
}

This isn't valid as of iOS 11.
O
Okan

I use tabBarController and i have a few section in my tableview at every tab, so this is best solution for me.

extension UITableView {

    func scrollToTop(){

        for index in 0...numberOfSections - 1 {
            if numberOfSections > 0 && numberOfRows(inSection: index) > 0 {
                scrollToRow(at: IndexPath(row: 0, section: index), at: .top, animated: true)
                break
            }

            if index == numberOfSections - 1 {
                setContentOffset(.zero, animated: true)
                break
            }
        }

    }

}

R
Rohit Gupta

I had to add the multiply by -1 * to the sum of the status bar and the navigation bar, because it was going that height off the screen,

self.tableView.setContentOffset(CGPointMake(0 , -1 * 
  (self.navigationController!.navigationBar.height +  
  UIApplication.sharedApplication().statusBarFrame.height) ), animated:true)

D
Daniel Galasko

in swift

your row = selectioncellRowNumber your section if you have = selectionNumber if you dont have set is to zero

//UITableViewScrollPosition.Middle or Bottom or Top

var lastIndex = NSIndexPath(forRow:  selectioncellRowNumber, inSection: selectionNumber)
self.tableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: UITableViewScrollPosition.Middle, animated: true)

K
Kavin Kumar Arumugam

Here Is The Code To ScrollTableView To Top Programatically

Swift:

self.TableView.setContentOffset(CGPointMake(0, 1), animated:true)

J
Jeni Khant

In Swift-3 :

self.tableView.setContentOffset(CGPoint.zero, animated: true)

G
Gary

The easiest way that I found to force UITableViewController to scroll-to-the-top under iPhone, iPad and macCatalyst (macOS) is as follows:

prepare to return 0 from [tableView:numberOfRowsInSection:]

call [self.tableView reloadData]

call [self.tableView layoutIfNeeded]

prepare to return the factual row count from [tableView:numberOfRowsInSection:]

call [self.tableView reloadData]


M
Matheus Veloza

If you i would like move scroll animation in the table, use this code. The scroll move to top with animation in .5 seconds.

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

[_tableContent scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];

[UIView commitAnimations];