I have a table view set up with a couple of different rows where each row contains two UITextViews. When the user starts editing a text view I want that table view cell where its located to be scrolled to the top of the table view. To scroll I am using:
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
This is working fine except for the last two rows, where the row is scrolled a little bit but not all the way to the top.
I think the issue is that the content size of the table view is not large enough to allow scrolling but I don't know how to adjust it in a proper way.
Any help would be appreciated.
Thanks in Advance!
Just call this method in viewDidLoad and your problem will get solved.
- (void)configureInsetsOfTableView
{
CGFloat defaultSpacing=10.0f;
[self.tableView setContentInset: UIEdgeInsetsMake(0.f, 0.f, self.navigationController.navigationBar.bounds.size.height + [UIApplication sharedApplication].statusBarFrame.size.height +defaultSpacing, 0.f)];
[self.tableView setScrollIndicatorInsets:UIEdgeInsetsMake(0.f, 0.f, self.navigationController.navigationBar.bounds.size.height + [UIApplication sharedApplication].statusBarFrame.size.height +defaultSpacing, 0.f)];
}
Related
I have a UITableView that is populated with cells of variable height. I would like the table to scroll to the bottom when the view is pushed into view.
I currently have the following function:
[self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
I'm using autolayout, dynamic table cells and UITableViewAutomaticDimension as row height. I set estimatedRowHeight = 100
The above code works fine in viewDidLoad however this has the unfortunate side effect of displaying the top of the table when the view first appears and then jumping to the bottom. I would prefer it if the table view could be scrolled to the bottom before it appears.
Please note: I'm loading data from core data.
Any guidance would be much appreciated, even if it's just a case of telling me what I have is all that is possible.
Theoretically, the order of the calls is correct: viewDidLoad should be called before the view is rendered, so the only thing that comes to my mind is that probably scrollToRowAtIndexPath:atScrollPosition:animated: is asynchronous. Maybe there's something you can do with UIScrollViewDelegate (remember, UITableView inherits from UIScrollView) - https://developer.apple.com/documentation/uikit/uiscrollviewdelegate?language=objc
Maybe you can do something like hiding the view on onLoad and showing it on scrollViewDidScroll:. I don't have time to test and paste a snippet but... maybe it helps :) Good luck!
This seems pretty reliable...
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.estimatedRowHeight = 100;
[self.tableView performBatchUpdates:nil completion:^(BOOL b) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:numRows - 1 inSection:numSections - 1];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
}];
}
Replace numRows and numSections with your data count(s).
I have a custom cell which should be spaced from the edges of the display. For that I use this:
- (void)setFrame:(CGRect)frame {
frame.origin.x += kCellSidesInset;
frame.size.width -= 2 * kCellSidesInset;
[super setFrame:frame];
}
I do have a button that hides/shows the bottom view of a stacked view inside the cell. For which I use this code:
- (IBAction)showCardDetails:(id)sender {
UITableView *cellTableView = (UITableView*)[[[[sender superview] superview] superview] superview ];
[cellTableView beginUpdates];
self.details.hidden = !self.details.hidden;
[cellTableView endUpdates];
// [cellTableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationBottom];
// [cellTableView reloadData];
}
However when the table is updated to reflect the change the right padding becomes allot bigger. Then when I scroll a bit it gets fixed.
As much as I could visually judge it is like 3 times. Maybe it adds two more kCellSidesInset on the right but I doubt it.
Why is this happening and how can it be fixed? Maybe it can be avoid by instead of giving inset to the cell giving it to the UITableView (I have some trouble figuring how to do this).
PS. All the code is inside the CustomCell.m. I am open for a suggestion to a better way of getting the UITableView inside the action. Should I use selector in the CustomTableViewController.m to implement the action there when the cell is added?
EDIT: From what I can see the re rendering of the cells goes trough three phases.
Phase one, a couple of these:
Phase two, it updates the view cells:
And here everything looks good for now. The view that I want to hide/show is hidden/shown and all is good but then some sort of cleanup breaks the layout:
I solved the problem by refactoring the setFrame method to use the superview's frame of the cell as a reference point for the cell frame
- (void)setFrame:(CGRect)frame {
frame.origin.x = self.superview.frame.origin.x + kCellSidesInset;
frame.size.width = self.superview.frame.size.width - 2 * kCellSidesInset;
[super setFrame:frame];
}
I have a tableview with dynamically sized cells, and a button that toggles the sort order of these cells. I'd like to scroll to the top every time the sort order is toggled, but when I set the content offset to the top, it seems to only scroll ~90% of the way there.
The offsetting code is simple enough and has served me well on different projects, so I seriously doubt the problem is here:
- (void) scrollToTop
{
CGPoint offset = CGPointMake(0, -self.tableView.contentInset.top);
[self.tableView setContentOffset:offset animated:YES];
}
[self.tableView reloadData]; // Lets update with whatever info we have
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
reloading and then scrolling resolved for me.
Didn't think I would find the answer so soon.
I was using UITableView's tableView:estimatedHeightForRowAtIndexPath: to return my minimum cell height, and it seems that the tableview uses this inside reloadData to create an idea of how big the content is before actually dequeuing the cells and caching their height. Being halfway down the content and reloading the data causes the tableview to think the distance to the top is the (number of cells offscreen above the current visible * the minimum height from estimatedHeightForRow), causing the tableview to only offset itself as if all cells were the minimum height. My solution was just to avoid using the estimated height, since my tableview isn't excessively long anyway. If you do have a large tableview (approaching 1000+ rows) that actually needs to use the estimated values for performance reasons, you might want to find a way to make the estimated values as close to the runtime values as possible, or look into more detailed solutions.
tl;dr - Remove tableView:estimatedHeightForRowAtIndexPath: and just allow the tableView to size itself from heightForRowAtIndexPath
What about something like this instead?
NSIndexPath *start = = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView scrollToRowAtIndexPath:start atScrollPosition:UITableViewScrollPositionTop animated:YES];
None of these worked for me. The solution was to call layoutIfNeeded() before setting the content offset:
tableView.reloadData();
tableView.layoutIfNeeded();
tableView.setContentOffset(CGPoint.Empty, animated: true);
all I am doing one chat application, i which i am using UITable view to display reply and response from user.in this case after some interval of time i am reloading my tableview to fetch new data from server. But the problem is that after adding new content to table view it will go at the bottom of table view and i have to scroll table view to see that one.or in other case whenever i am reloading my table it will show its first cell on view. Now my question is "is it possible to load last cell of UITableview after view gets load or reload table view?"
on search I find this line but it give me error
[sTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:sender.tag inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
what is sender.tag in this line? // use of undeclared identifier sender
This line working well but scrolling the page which i dont want
[table_readText scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES ];
Any idea or suggestion would be highly welcome.
Thanks in advance..
You can also look at the scroll view's setContentOffset:animated: method.
Going to the top would mean,
[self.tableView setContentOffset:CGPointZero animated:YES];
and the bottom would be,
CGFloat height = self.tableView.contentSize.height - self.tableView.bounds.size.height;
[self.tableView setContentOffset:CGPointMake(0, height) animated:YES];
SECOND option:
scrollToRowAtIndexPath:atScrollPosition:animated:
Scrolls the receiver until a row identified by index path is at a particular location on the screen.
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
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.