I am trying to scroll the table to last row using contentOffset method.This method works fine when data is not much but if user has scrolled to much it does take table to last cell. I am using the following code
self.delegate.table.setContentOffset(CGPointMake(0, table.contentSize.height - table.frame.size.height), animated: false)
I think the preferred way would be calling
func scrollToRow(at indexPath: IndexPath,
at scrollPosition: UITableViewScrollPosition,
animated: Bool)
at the UITableView, see here
Why not use the scrollToRow method.
let lastIndex:NSIndexPath = NSIndexPath(forRow: tableView.numberOfRowsInSection(0) - 1, inSection: 0)
tableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: .Middle, animated: true)
The time this takes to scroll is based on the contentSize. Strangely this time is still used when animated is set to false. To make it more snappy set your own duration in an animation block.
UIView.animateWithDuration(aSmallValue) {
self.tableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: .Middle, animated: true)
}
Try this it works for me,
code for objective-c
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
Code for Swift 3.0
let scrollIndexPath:IndexPath = IndexPath(row: (tblView.numberOfRows(inSection: 0)-1), section: 0)
tblView.scrollToRow(at: scrollIndexPath, at: .bottom, animated: false)
let offsetOfTable = CGPoint(x:0, y:self.yourTableView.contentSize.height - self.yourTableView.frame.size.height + self.FOOTER_HEIGHT);
self.yourTableView.contentOffset = offsetOfTable;
Related
In order to achieve scroll To bottom for a table view, I am using the below code.
extension UITableView {
func scrollToBottom(){
let indexPath = IndexPath(
row: self.numberOfRows(inSection: self.numberOfSections -1) - 1,
section: self.numberOfSections - 1)
self.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
This is working perfectly fine for all the devices whose versions are below 13, but in ios 13 it is not scrolling completely to last cell , it is stopping in between the last cell (approximate 40 pixel from bottom).
I also tried alternate ways by
setting content Offset
setting the scroll to visible rect all
having a delay for 1.0 seconds
but all of these having the same behaviour, not scrolling completely.
If you're facing this issue because of having different cells with different heights then below code will probably work for you:
private func moveTableViewToBottom(indexPath: IndexPath) {
tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
}
}
Try this
func scrollToBottom(){
DispatchQueue.main.async {
let indexPath = IndexPath(row: self.yourDataSourceArray-1, section: self.numberOfSections - 1)
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
Thank Shivam Pokhriyal
It helps me work properly on iOS 13, But I don't know exactly why
Swift:
private func moveTableViewToBottom(indexPath: IndexPath) {
tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
if #available(iOS 13.0, *) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
}
}
}
OC:
- (void)scrollToBottomAnimated:(BOOL)animated {
NSInteger rows = [self.tableView numberOfRowsInSection:0];
if (rows > 0) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rows-1 inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:animated];
if (#available(iOS 13.0, *)) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSInteger rows = [self.tableView numberOfRowsInSection:0];
if (rows > 0) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rows-1 inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:animated];
}
});
} else {
// Fallback on earlier versions
}
}
}
I am creating a chat interface.
User's message is put in a new UITable view cell.
And when update the table view, I use the following code.
extension UITableView {
func scrollToBottom() {
let rows = self.numberOfRows(inSection: 0)
if rows > 0 {
let indexPath = IndexPath(row: rows - 1, section: 0)
self.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
}
Actually this works a little better, but there is something strange.
When I turn off the app and turn it on again, or after exiting the screen and entering again, the following issues arise.
The issue is that when I add a new cell, it goes up to the first cell in the table view and back down to the last cell.
See the issue(https://vimeo.com/266821436)
As the number of cells increases, the scrolling becomes too fast and too messy.
I just want to keep updating the last cell that is newly registered.
What should I do?
Please use DispatchQueue to Scroll because of the method you are fire is executed with tableView load data so we need to give time to scroll.
extension UITableView {
func scrollToBottom() {
let rows = self.numberOfRows(inSection: 0)
if rows > 0 {
DispatchQueue.main.async {
let indexPath = IndexPath(row: rows - 1, section: 0)
self.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
}
}
or
func scrollToBottom(){
DispatchQueue.main.async {
let indexPath = IndexPath(row: self.array.count-1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
self.scrollToRow(at: indexPath.last ...)
I am trying to scroll horizontally to a collectionView when I call a function and am receiving the error 'none' is unavailable: use [] to construct an empty option set - not sure what is wrong here...Thanks in advance!
func handleSearch() {
scrollToMenuIndex(menuIndex: 2)
}
func scrollToMenuIndex(menuIndex: Int) {
let indexPath = NSIndexPath(item: menuIndex, section: 0)
collectionView?.scrollToItem(at: indexPath as IndexPath, at: .none, animated: true)
}
You have to provide a position where the item should be scrolled to. See the documentation for UICollectionViewScrollPosition for possible values. In the meantime you could use this:
collectionView?.scrollToItem(at: indexPath as IndexPath, at: .left, animated: true)
I am attempting to make the last row in a UITableView visible, after it has been added. Right now, when I add a row and call reloadData, the table goes to the top.
I figure if I get the indexPath for the last row, that I can select that row and it should appear in the list. I am unsure of how to get that value, or even if I am approaching this correctly.
How do I get an indexPath for a specific row?
Please note that, you don't need to call the reloadData to make the last row visible. You can make use of scrollToRowAtIndexPath method.
You can use the below code to achieve your goal.
// First figure out how many sections there are
let lastSectionIndex = self.tblTableView!.numberOfSections() - 1
// Then grab the number of rows in the last section
let lastRowIndex = self.tblTableView!.numberOfRowsInSection(lastSectionIndex) - 1
// Now just construct the index path
let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex)
// Make the last row visible
self.tblTableView?.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true)
Swift 4.0:
tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.none, animated: true)
You can use scrollToRowAtIndexPath with extension:
In Swift 3:
extension UITableView {
func scrollToLastCell(animated : Bool) {
let lastSectionIndex = self.numberOfSections - 1 // last section
let lastRowIndex = self.numberOfRows(inSection: lastSectionIndex) - 1 // last row
self.scrollToRow(at: IndexPath(row: lastRowIndex, section: lastSectionIndex), at: .Bottom, animated: animated)
}
}
Shamsudheen TK's answer will crash
if there is no rows/sections in tableview.
The following solution to avoid crash at run time
extension UITableView {
func scrollToBottom() {
let lastSectionIndex = self.numberOfSections - 1
if lastSectionIndex < 0 { //if invalid section
return
}
let lastRowIndex = self.numberOfRows(inSection: lastSectionIndex) - 1
if lastRowIndex < 0 { //if invalid row
return
}
let pathToLastRow = IndexPath(row: lastRowIndex, section: lastSectionIndex)
self.scrollToRow(at: pathToLastRow, at: .bottom, animated: true)
}
}
Note: If you are trying to scroll to bottom in block/clousure then you need to call this on main thread.
DispatchQueue.main.async {
self.tableView.scrollToBottom()
}
Hope this will helps other
As suggested by others get indexPath for perticular sections like section 0.
After that call...add this methos in cellFOrROwAtIndex
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES]; ..to scroll to specific indexPath in TableView.
Note:-But it still need scrolling of tableview in Downward direction.
You shouldn't be using -reloadData for this use case. What you're looking for is -insertRowsAtIndexPaths:withRowAnimation:.
Feel free to ask if you want some usage examples or a more detailed explanation as to why using -reloadData send you to the top of the UITableView.
Not mandatorily required to get the indexpath of last row.
You can set the CGPoint of UITableview to show a last row you added.
I always use this code in my chat application to show a last added message.
//Declaration
#IBOutlet weak var tableview: UITableView!
//Add this executable code after you add this message.
var tblframe: CGRect = tableview.frame
tblframe.size.height = self.view.frame.origin.y
tableview.frame = tblframe
var bottomoffset: CGPoint = CGPointMake(0, tableview.contentSize.height - tableview.bounds.size.height)
if bottomoffset.y > 0 {
tableview.contentOffset = bottomoffset;
}
I hope it will work for you.
Thanks.
Swift 5.0 +
extension UITableView {
func isLastVisibleCell(at indexPath: IndexPath) -> Bool {
guard let lastIndexPath = indexPathsForVisibleRows?.last else {
return false
}
return lastIndexPath == indexPath
}
}
I have a custom class (superclass UITableViewController).
I have an add button that:
Begins table updates.
Inserts row in table.
Ends table updates.
Calls selectRowAtIndexPath.
Calls didSelectRowAtIndexPath.
Unfortunately, didDeselectRowAtIndexPath is not being called after programatically selecting a row.
Note: Using Swift (not Objective-C).
#IBAction func addButton(sender: UIBarButtonItem) {
let indexPathZero: NSIndexPath = NSIndexPath(forRow: 0, inSection: 0)
todoController.addNewTodoItem(nameOfItem: "test5")
self.tableView.beginUpdates()
self.tableView.insertRowsAtIndexPaths([indexPathZero], withRowAnimation: UITableViewRowAnimation.Top)
self.tableView.contentOffset.y = 64
self.tableView.endUpdates()
// Ask user for input
editCell(indexPathZero)
}
func editCell(indexPath: NSIndexPath) {
self.tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.None)
self.tableView(self.tableView, didSelectRowAtIndexPath: indexPath)
}
Solution
let indexPathZero: NSIndexPath = NSIndexPath(forRow: 0, inSection: 0)
todoController.addNewTodoItem(nameOfItem: "test5")
let currSelectedIndexPath = self.tableView.indexPathForSelectedRow()
if currSelectedIndexPath != nil {
self.tableView.deselectRowAtIndexPath(currSelectedIndexPath!, animated: true)
self.tableView(self.tableView, didDeselectRowAtIndexPath: currSelectedIndexPath!)
}
self.tableView.beginUpdates()
self.tableView.insertRowsAtIndexPaths([indexPathZero], withRowAnimation: UITableViewRowAnimation.Top)
self.tableView.contentOffset.y = 64
self.tableView.endUpdates()
// Ask user for input
editCell(indexPathZero)
See the apple docs. When you call selectRowAtIndexPath, it will not call didSelectRowAtIndexPath.
"Calling this method does not cause the delegate to receive a tableView:willSelectRowAtIndexPath: or tableView:didSelectRowAtIndexPath: message, nor does it send UITableViewSelectionDidChangeNotification notifications to observers."