I have a UITableView where I add rows dynamically. Basically a chat app.
I need to scroll the UITableView to bottom after reloadData
My code is:
It scrolls to here:
https://dl-web.dropbox.com/get/Screen%20Shot%202014-12-05%20at%2013.04.23.png?_subject_uid=44249794&w=AADgJkJJOGK_ANH_cXWnxeXmhFmF5KHjZllg-kP66HARdw
When manually scrolling, I can scroll even further to here: https://dl-web.dropbox.com/get/Screen%20Shot%202014-12-05%20at%2013.05.19.png?_subject_uid=44249794&w=AADtQEfMHMy0Ounri5W3gBTLNgR4uckT_gBdYQel9vD1qQ
My code:
[_chatTable reloadData];
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: [[AppData getChatLog]count]-1 inSection: 0];
[_chatTable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
I need the UITableView to scroll all the way to the bottom.
Sorry,I couldn't open up the given links.Try this
UITableView *yourTableView = yourTableViewToBeSetHere;
CGRect lastCellRect = CGRectMake(0, CGFLOAT_MAX, 50, 50);
// Scroll to the particular rect
[yourTableView scrollRectToVisible:lastCellRect
animated:YES];
Good luck!
Related
I have two UICollectionViews and when the user scrolls on one, the other should also change by scrolling to the same index path. I've implemented the following code so that, when the first UICollectionView decelerates and lands on one cell, the second UICollectionView should automatically scroll to that same IndexPath:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
// Only do this for the first UICollectionView
if (scrollView.tag == 0) {
// Find the new IndexPath
CGRect visibleRect = (CGRect){.origin = self.filterCollectionView.contentOffset, .size = self.filterCollectionView.bounds.size};
CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
NSIndexPath *visibleIndexPath = [self.filterCollectionView indexPathForItemAtPoint:visiblePoint];
// On main thread, scroll second CollectionView to visibleIndexPath
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Row: %li, Sec: %li", visibleIndexPath.row, visibleIndexPath.section);
[self.filterTitleCollectionView reloadData];
[self.filterTitleCollectionView layoutIfNeeded];
[self.filterTitleCollectionView setNeedsDisplay];
//scroll titles to same index path
[self.filterTitleCollectionView scrollToItemAtIndexPath:visibleIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
NSLog(#"Row: %li, Sec: %li", self.filterTitleCollectionView.indexPathsForSelectedItems[0].row, self.filterTitleCollectionView.indexPathsForSelectedItems[0].section);
});
}
}
For some reason, the filterTitleCollectionView does not scroll to the visibleIndexPath. Or if it does scroll, it scrolls to the wrong cell (in a predictable pattern). You can see the video of it here:
https://streamable.com/q2lnnw
The row number is in the title of the cell. As you can see, the second UICollectionView keeps scrolling to row 2 for some reason.
The correct IndexPath is printed, but it scrolls to the wrong IndexPath or doesn't scroll at all.
I have a button in a UITableViewCell which is stored as an ivar called currentButton. When that button is clicked a UIView containing a UIPickerView and UIToolBar is invoked and shown from the bottom of the screen. However, I have looked at other posts for hours and this following code still doesn't work. The cells sometimes scroll down below the keyboard and the cells all the way at the bottom of the UITableView do not scroll up enough to go on top of the UIView backgroundPickerView. This is my code:
CGPoint center = currentButton.center;
CGPoint rootViewPoint = [currentButton.superview convertPoint:center toView:self.view];
NSIndexPath *indexPath = [measurementTableView indexPathForRowAtPoint:rootViewPoint];
CGRect cellRect = [measurementTableView rectForRowAtIndexPath:indexPath];
if (cellRect.origin.y + cellRect.size.height >= backgroundPickerView.frame.origin.y) {
[measurementTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
CGPoint offset = measurementTableView.contentOffset;
offset.y += ((cellRect.origin.y - cellRect.size.height) - backgroundPickerView.frame.origin.y);
[measurementTableView setContentOffset:offset animated:YES];
}
Does anyone see what I am doing wrong here?
UITableViewCell are reused,keeping a reference of a UITableViewCell's subView is not a good approach.
If the special UITableViewCell is not in UITableView's visibleCells ,its frame is undefined.
A solution is:
Create a custom UITableViewCell that has the same structure as you need.
Keep a reference of the indexpath for the custom UITableViewCell.
Use this indexpath to do the work .
I hope this will work:
[measurementTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
CGPoint offset = measurementTableView.contentOffset;
offset.y += backgroundPickerView.frame.height;
This code worked for me. Uses a different approach than the ones suggested here and it's good for what I need.
CGRect buttonFrame = [currentButton convertRect:currentButton.bounds toView:measurementTableView];
NSIndexPath *indexPath = [measurementTableView indexPathForRowAtPoint:buttonFrame.origin];
CGRect backPickFrame = [backgroundPickerView convertRect:backgroundPickerView.bounds toView:measurementTableView];
if (buttonFrame.origin.y + buttonFrame.size.height >= backPickFrame.origin.y) {
measurementTableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, backgroundPickerView.frame.size.height, 0.0);
[measurementTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
} else {
[measurementTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
First problem is that UITableView is partially hidden under a keyboard. So when you trying scroll to bottom rows table will bounce back and required content is not visible.
To solve this problem when keyboard appears/hides you have two choices.
update value of self.tableView.contentInstet.bottom to take into account keyboard size
update frame of table, if you are using auto-layout then best approach will be update constant part of bottom constraint (IMO this is better approach).
After you did that you can do this:
CGRect rectToBeVisible = [currentButton convertRect: currentButton.bounds
toView: self.tableView];
[self.tableView scrollRectToVisible: rectToBeVisible animated: YES];
I could messed up view to which convertion should be performed, so experiment a bit, but general concept should be clear.
I have this line
[self.tableView scrollToRowAtIndexPath:self.cellIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
The cellIndexPath is section 2, row 0.
Instead of scrolling to the row it scrolls to the header of section 2.
Testing shows this seems to be the default behavior. Is there any way to override it and actually scroll to row 0?
Thanks
You could get the cell's rectangle and then scroll to that.
CGRect cellRect = [myTableView rectForRowAtIndexPath:cellIndexPath];
[myTableView setContentOffset:CGPointMake(0,(cellRect.origin.y)) animated:YES];
I try to Build UITableView That load every time a 5 object and when I notice that the scroll table in the last position I reload table data and this open the table from the top again.
how I can save the position and when table reload back to last cell I see ?
Use the below code before you reload the table data
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(numberOfRowsInLastSection - 1) inSection:(numberOfSections - 1)];
By the above line you will get the position of last row of table.
Use this code after reloading the tableView
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
This will scroll to already saved position.
I find a solution in this post - UITableView , Scroll to bottom on reload?
[postTableView reloadData];
NSIndexPath* ipath = [NSIndexPath indexPathForRow: oldSkip -1 inSection: 1];
[postTableView scrollToRowAtIndexPath: ipath atScrollPosition: UITableViewScrollPositionTop animated:YES];
Here is the code I use:
//inserting a row at the bottom first
_numberOfRecords++;
[_tableView beginUpdates];
[_tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:_numberOfRecords-1 inSection:0]] withRowAnimation:UITableViewRowAnimationBottom];
[_tableView endUpdates];
//clear text
_inputField.text = #"";
//then scroll to bottom
CGPoint bottomOffset = CGPointMake(0, _tableView.contentSize.height + 44.0 + _tableView.contentInset.top - _tableView.bounds.size.height);
NSLog(#"%f", _tableView.contentSize.height + 44.0 + _tableView.contentInset.top - _tableView.bounds.size.height);
[_tableView setContentOffset:bottomOffset animated:YES];
This would scroll the tableview in a very strange way.
But if I put the scrolling code BEFORE the insertion, it works fine except that it ignores the latest inserted row. That is, it scrolls to the second last row instead of scrolling to the very last row (of course, because it scrolls before inserting a new roll.)
So I believe this code has no problem of the position where it should scroll to.
The problem probably comes from row insertion to tableview.
It violates the animation of scrolling the table view.
I am doing this to make a chatting view.
Each time the user sends or receives a message, I insert a row containing the message to a table view, and scrolls it to the bottom. That's why I use tableView here. I tried to use scrollView with label, it works fine, but tableView seems more popular in a chatting view.
I was thinking to use scrollView or tableView, and I found the built-in message app of Apple is using a tableView, so I adopt tableView. Let me know if a scrollView with Label is better than a tableView.
Anyway, how can I scroll a tableView to the bottom after inserting a new row?
Try using UITableView's scrollToRowAtIndexPath::
[self.tableView scrollToRowAtIndexPath: atScrollPosition: animated:];
This is my own solution:
[_tableView reloadData];
//scroll to bottom
double y = _tableView.contentSize.height - _tableView.bounds.size.height;
CGPoint bottomOffset = CGPointMake(0, y);
NSLog(#"after = %f", y);
if (y > -_tableView.contentInset.top)
[_tableView setContentOffset:bottomOffset animated:YES];
Firstly reloadData after endUpdates. This ensures the tableView contentSize is updated after inserting a new row. Then check if the scrolling distance is greater than the contentInset.top (this is for avoiding the tableview hiding behind the status bar and navigation bar) then to scroll down, otherwise not to scroll because of some weird animation.
Alternatively, you can simply use
[self.tableView scrollToRowAtIndexPath: inSection: atScrollPosition: animated:];
to scroll to the row you want. But this doesn't handle cells with sections and footers very well. For plain tableViewCell, you can just use this to do the magic. Otherwise you may find my trick solution performs better.
Anyway, thanks for all your answers.