How to scroll to top in IOS7 UITableView? - ios

In IOS6 I have the following code to scroll to the top of a UITableView
[tableView setContentOffset:CGPointZero animated:YES];
In IOS7 this doesn't work anymore. The table view isn't scrolled completely to the top (but almost).

In iOS7, whole screen UITableView and UIScrollView components, by default, adjust content and scroll indicator insets to just make everything work. However, as you've noticed CGPointZero no longer represents the content offset that takes you to the visual "top".
Use this instead:
self.tableView.contentOffset = CGPointMake(0, 0 - self.tableView.contentInset.top);
Here, you don't have to worry about if you have sections or rows. You also don't tell the Table View to target the first row, and then wonder why it didn't show all of your very tall table header view, etc.

Try this:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];

Based on the accepted answer from #Markus Johansson, here is the Swift code version:
func scrollToTop() {
if (self.tableView.numberOfSections > 0 ) {
let top = NSIndexPath(row: Foundation.NSNotFound, section: 0)
self.tableView.scrollToRow(at: top as IndexPath, at: .top, animated: true);
}
}

By the help from some other answers here I managed to get it working. To avoid a crash I must first check that there are some sections. NsNotFound can be used as a row index if the first section has no rows. Hopefully this should be a generic function to be placed in a UITableViewController:
-(void) scrollToTop
{
if ([self numberOfSectionsInTableView:self.tableView] > 0)
{
NSIndexPath* top = [NSIndexPath indexPathForRow:NSNotFound inSection:0];
[self.tableView scrollToRowAtIndexPath:top atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}

var indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.sampleTableView.scrollToRowAtIndexPath(indexPath,
atScrollPosition: UITableViewScrollPosition.Top, animated: true)
or
self.sampleTableView.setContentOffset(CGPoint.zero, animated:false)

float systemVersion= [[[UIDevice currentDevice] systemVersion] floatValue];
if(systemVersion >= 7.0f)
{
self.edgesForExtendedLayout=UIRectEdgeNone;
}
Try this code in viewDidLoad() method.

Here is idStar's answer in updated Swift 3 syntax:
self.tableView.contentOffset = CGPoint(x: 0, y: 0 - self.tableView.contentInset.top)
With animation:
self.tableView.setContentOffset(CGPoint(x: 0, y: 0 - self.tableView.contentInset.top), animated: true)

Swift 3
If you have table view headers CGPointZero may not work for you, but this always does the trick to scroll to top.
self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: UITableViewScrollPosition.top, animated: false)

you can still use scrollToRowAtIndexPath: for the purpose

I realize this has been answered but I just wanted to give another option:
CGRect frame = {{0, 0},{1, 1}};
[self.tableView scrollRectToVisible:frame animated:YES];
This always guarantees the UITableView will scroll to the top. The accepted answer:
NSIndexPath* top = [NSIndexPath indexPathForRow:NSNotFound inSection:0];
[self.tableView scrollToRowAtIndexPath:top atScrollPosition:UITableViewScrollPositionTop animated:YES];
was not working for me because I was scrolling to a tableHeaderView and not a cell.
Using scrollRectToVisible works on iOS 6 and 7.

Swift 4 UITableViewExtension:
func scrollToTop(animated: Bool) {
if numberOfSections > 0 {
let topIndexPath = IndexPath(row: NSNotFound, section: 0)
scrollToRow(at: topIndexPath, at: .top, animated: animated)
}
}

in swift I used:
self.tableView.setContentOffset(CGPointMake(0, 0), animated: true)
but #Alvin George's works great

Related

How to show the middle element of array in the collection view when the app is launched?

Can Somebody please tell me, How to show the middle element of array in the collection view when the app is launched and other elements are show in the left and right side of this middle element?
In viewDidLayoutSubviews method scroll your collection view to that particular IndexPath. And for do it for first time when launch use bool variable.
var isFirstTime = true
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if isFirstTime {
isFirstTime = false
let selectedIndex = IndexPath(item: 0, section: 0)
self.collectionView.scrollToItem(at: selectedIndex, at: .centeredHorizontally, animated: false)
}
}
You can use [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:(myArr.count/2) inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
Change the section in index path accordingly.

scrollToRowAtIndexPath:atScrollPosition causing table view to "jump"

My app has chat functionality and I'm feeding in new messages like this:
[self.tableView beginUpdates];
[messages addObject:msg];
[self.tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:messages.count - 1 inSection:1]] withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView endUpdates];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:messages.count - 1 inSection:1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
However, my table view "jumps" weirdly when I'm adding a new message (either sending and receiving, result is the same in both):
Why am I getting this weird "jump"?
OK, I figured it out. As you say, the problem has to do with auto-sizing cells. I used two tricks to make things work (my code is in Swift, but it should be easy to translate back to ObjC):
1) Wait for the table animation to finish before taking further action. This can be done by enclosing the code that updates the table within a block between CATransaction.begin() and CATransaction.commit(). I set the completion block on CATransaction -- that code will run after the animation is finished.
2) Force the table view to render the cell before scrolling to the bottom. I do it by increasing the table's contentOffset by a small amount. That causes the newly inserted cell to get dequeued, and its height gets calculated. Once that scroll is done (I wait for it to finish using the method (1) above), I finally call tableView.scrollToRowAtIndexPath.
Here's the code:
override func viewDidLoad() {
super.viewDidLoad()
// Use auto-sizing for rows
tableView.estimatedRowHeight = 40
tableView.rowHeight = UITableViewAutomaticDimension
tableView.dataSource = self
}
func chatManager(chatManager: ChatManager, didAddMessage message: ChatMessage) {
messages.append(message)
let indexPathToInsert = NSIndexPath(forRow: messages.count-1, inSection: 0)
CATransaction.begin()
CATransaction.setCompletionBlock({ () -> Void in
// This block runs after the animations between CATransaction.begin
// and CATransaction.commit are finished.
self.scrollToLastMessage()
})
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([indexPathToInsert], withRowAnimation: .Bottom)
tableView.endUpdates()
CATransaction.commit()
}
func scrollToLastMessage() {
let bottomRow = tableView.numberOfRowsInSection(0) - 1
let bottomMessageIndex = NSIndexPath(forRow: bottomRow, inSection: 0)
guard messages.count > 0
else { return }
CATransaction.begin()
CATransaction.setCompletionBlock({ () -> Void in
// Now we can scroll to the last row!
self.tableView.scrollToRowAtIndexPath(bottomMessageIndex, atScrollPosition: .Bottom, animated: true)
})
// scroll down by 1 point: this causes the newly added cell to be dequeued and rendered.
let contentOffset = tableView.contentOffset.y
let newContentOffset = CGPointMake(0, contentOffset + 1)
tableView.setContentOffset(newContentOffset, animated: true)
CATransaction.commit()
}
Change UITableViewRowAnimationBottom to UITableViewRowAnimationNone and try
Try This!
UITableViewRowAnimation rowAnimation = UITableViewRowAnimationTop;
UITableViewScrollPosition scrollPosition = UITableViewScrollPositionTop;
[self.tableView beginUpdates];
[messages addObject:msg];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:rowAnimation];
[self.tableView endUpdates];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:YES];
// Fixes the cell from blinking (because of the transform, when using translucent cells)
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
For Swift 3 and 4
for scroll down to bottom of table View automatically when add new item in the table view just in tableView function add following line its works me
tableView.scrollToRow(at: IndexPath, at: .bottom, animated: true)
for example
in my case I have only one section so 0 is use for section and I have list of orderItems so for last index I use orderItems.count - 1
tableView.scrollToRow(at: [0, orderItems.count - 1], at: .bottom, animated: true)
I've just found out that on ios 11 this problem no longer exists. So there's no longer a content jump when adding a row to a table view and then scrolling to it with scrollToRow(at:) .
Also, on ios 10 calling scrollToRowAtIndexPath with animated=false fixes the content jump

[iOS]UITableView using auto layout, how to scroll to bottom [duplicate]

I have a UITableView with cells that are dynamically updated. Everything works fine apart from when tableview.reload is called (see below) to refresh the cells in the table I would like the table to scroll to the bottom to show the new entries.
- (void)reloadTable:(NSNotification *)notification {
NSLog(#"RELOAD TABLE ...");
[customTableView reloadData];
// Scroll to bottom of UITable here ....
}
I was planning to use scrollToRowAtIndexPath:atScrollPosition:animated: but then noticed that I don't have access to an indexPath.
Does anyone know how to do this, or of a delegate callback that I could use?
Use:
NSIndexPath* ipath = [NSIndexPath indexPathForRow: cells_count-1 inSection: sections_count-1];
[tableView scrollToRowAtIndexPath: ipath atScrollPosition: UITableViewScrollPositionTop animated: YES];
Or you can specify the section index manually (If one section => index=0).
Another solution is to flip the table vertically, and flip each cell vertically:
Apply the transform to the UITableView when initializing:
tableview.transform = CGAffineTransformMakeScale(1, -1);
and in cellForRowAtIndexPath:
cell.transform = CGAffineTransformMakeScale(1, -1);
This way you don't need workarounds for scrolling issues, but you will need to think a little harder about contentInsets/contentOffsets and header/footer interactions.
-(void)scrollToBottom{
[self.tableView scrollRectToVisible:CGRectMake(0, self.tableView.contentSize.height - self.tableView.bounds.size.height, self.tableView.bounds.size.width, self.tableView.bounds.size.height) animated:YES];
}
//In swift
var iPath = NSIndexPath(forRow: self.tableView.numberOfRowsInSection(0)-1,
inSection: self.tableView.numberOfSections()-1)
self.tableView.scrollToRowAtIndexPath(iPath,
atScrollPosition: UITableViewScrollPosition.Bottom,
animated: true)
Swift 3
For all the folks here trying to figure out how to solve this problem the key is to call the .layoutIfNeeded() method after .reloadData() :
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.setContentOffset(CGPoint(x: 0, y: tableView.contentSize.height - tableView.frame.height), animated: false)
I was working with multiple sections in UITableView and it worked well.
Fot Swift 5
extension UITableView {
func scrollToBottom(animated: Bool = true) {
let section = self.numberOfSections
if section > 0 {
let row = self.numberOfRows(inSection: section - 1)
if row > 0 {
self.scrollToRow(at: IndexPath(row: row-1, section: section-1), at: .bottom, animated: animated)
}
}
}
}
As this is something you might want to use really often, I suggest that you create a class extension on UITableView :
extension UITableView {
func scrollToBottom(animated: Bool = true) {
let section = self.numberOfSections
if section > 0 {
let row = self.numberOfRowsInSection(section - 1)
if row > 0 {
self.scrollToRowAtIndexPath(NSIndexPath(forRow: row - 1, inSection: section - 1), atScrollPosition: .Bottom, animated: animated)
}
}
}
}
This is another solution, worked well in my case, when cell height is big.
- (void)scrollToBottom
{
CGPoint bottomOffset = CGPointMake(0, _bubbleTable.contentSize.height - _bubbleTable.bounds.size.height);
if ( bottomOffset.y > 0 ) {
[_bubbleTable setContentOffset:bottomOffset animated:YES];
}
}
extension is better to be done on UIScrollView instead of UITableView, this way it works on scrollView, tableView, collectionView (vertical), UIWebView (inner scroll view), etc
public extension UIScrollView {
public func scrollToBottom(animated animated: Bool) {
let rect = CGRectMake(0, contentSize.height - bounds.size.height, bounds.size.width, bounds.size.height)
scrollRectToVisible(rect, animated: animated)
}
}
Swift 5
func scrollToBottom() {
let section = self.tableView.numberOfSections
let row = self.tableView.numberOfRows(inSection: self.tableView.numberOfSections - 1) - 1;
guard (section > 0) && (row > 0) else{ // check bounds
return
}
let indexPath = IndexPath(row: row-1, section: section-1)
self.tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
I dont agree that we should user cells_count,sections_count,self.dateSource.countand so on, Instead, the delegate will be better.
This is the best way.
- (void)scrollToBottom
{
CGFloat yOffset = 0;
if (self.tableView.contentSize.height > self.tableView.bounds.size.height) {
yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;
}
[self.tableView setContentOffset:CGPointMake(0, yOffset) animated:NO];
}
try this code, It may help you:
self.tableView.reloadData()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+0.1, execute: {
let indexPath = IndexPath(row: self.dateSource.count-1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.bottom, animated: true)
})

Scroll to top with estimated cell heights

What is the proper way of scrolling a UITableView to the top when using estimated cell heights by implementing tableView:estimatedHeightForRowAtIndexPath:?
I noticed that the usual method does not necessarily scroll to the top if there is enough estimation error.
[self.tableView setContentOffset:CGPointMake(0, 0 - self.tableView.contentInset.top) animated:animated];
I came across a similar issue (I wasn't trying to scroll the tableview to the top manually but the view wasn't scrolling correctly when tapping the status bar).
The only way I've come up with to fix this is to ensure in your tableView:estimatedHeightForRowAtIndexPath: method you return the actual height if you know it.
My implementation caches the results of calls to tableView:heightForRowAtIndexPath: for efficiency , so I'm simply looking up this cache in my estimations to see if I already know the real height.
I think the issue comes from tableView:estimatedHeightForRowAtIndexPath: being called in preference over tableView:heightForRowAtIndexPath: even when scrolling upwards over cells that have already been rendered. Just a guess though.
How about tableView.scrollToRow? Solved the issue for me.
Swift 3 example:
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
how about this snippet code
[UIView animateWithDuration:0.0f animations:^{
[_tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO]; //1
} completion:^(BOOL finished) {
_tableView.contentOffset = CGPointZero; //2
}];
scroll to offset that calculated by estimatedHeightForRowAtIndexPath
setContentOffsetZero
inspiration from https://github.com/caoimghgin/TableViewCellWithAutoLayout/issues/13
let point = { () -> CGPoint in
if #available(iOS 11.0, *) {
return CGPoint(x: -tableView.adjustedContentInset.left, y: -tableView.adjustedContentInset.top)
}
return CGPoint(x: -tableView.contentInset.left, y: -tableView.contentInset.top)
}()
for section in (0..<tableView.numberOfSections) {
if 0 < tableView.numberOfRows(inSection: section) {
// Find the cell at the top and scroll to the corresponding location
tableView.scrollToRow(at: IndexPath(row: 0, section: section),
at: .none,
animated: true)
if tableView.tableHeaderView != nil {
// If tableHeaderView != nil then scroll to the top after the scroll animation ends
CATransaction.setCompletionBlock {
tableView.setContentOffset(point, animated: true)
}
}
return
}
}
tableView.setContentOffset(point, animated: true)

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?
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)
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];
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)
}
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.
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)
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)
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)
}
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
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];
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)];
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.
Swift:
tableView.setContentOffset(CGPointZero, animated: true)
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()
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)
}
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)
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)
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)
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()
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)
}
}
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 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
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
Swift 4 via extension, handles empty table view:
extension UITableView {
func scrollToTop(animated: Bool) {
self.setContentOffset(CGPoint.zero, animated: animated);
}
}
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
}
}
}
}
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)
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)
Here Is The Code To ScrollTableView To Top Programatically
Swift:
self.TableView.setContentOffset(CGPointMake(0, 1), animated:true)
In Swift-3 :
self.tableView.setContentOffset(CGPoint.zero, animated: true)
If you need use Objective-C or you still in love:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
Solution with scrollToRow which fixes the problem for empty TableView (needs for search).
import UIKit
extension UITableView {
public func scrollToTop(animated: Bool = false) {
if numberOfRows(inSection: 0) > 0 {
scrollToRow(
at: .init(row: 0, section: 0),
at: .top,
animated: animated
)
}
}
}

Resources