Scrolling UITableView to specific position using setContentOffset not working properly - ObjC - ios

I saw many answers in SO which describes how can I scroll UITableView to the specific position.
I use setContentOffset for scrolling to particular position.
I meet a weird issue. If my tableView is half scrolled then setContentOffset is working properly. But when I scroll to end of the tableView and then use setContentOffset, table view scrolls a little bit more.
My setContentOffset value is CGPoint(0,199).
[self.tableView setContentOffset:CGPointMake(0,199) animated:NO];
After reaching a bottom I use setContentOffset. Then I check the contentOffset of UITableView. and it is 169.
I am not able to figure it out what exactly the issue is.
Edit::
I am using the following code. When a button in the last cell is pressed:
- (void)userPressedSubmitButtonOnLastCell
{
[self updateData];
[self.tableView reloadData];
[self performSelector:#selector(scrollTableView) withObject:nil afterDelay:0.5];
}
- (void)scrollTableView
{
[self.tableView setContentOffset:CGPointMake(0,199) animated:NO];
}

Why don't you use tableView scrollToRowAtIndexPath: or tableView scrollRectToVisible:(CGRect)? This will allow you to scroll to either specific row or rectangle. ContentOffset is more suitable for UIScrollView.

Related

How to scroll to the bottom of a UITableView on the iPhone without seeing the scroll

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).

Insert cell at indexPath, not working correctly

I have a custom UITableViewCell with objects in it (such as UILabel, UITextView, UITextField etc.). When a button gets selected, a cell gets added to the tableView.
When I run it on the simulator, and the cell gets added, all the visible cell's and subviews height get really compact. (I do have auto constraint applied.)
....
[[self myTableView] insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationTop];
If I do the following, the cells get back to normal:
NSArray* visibleCellIndex = self.myTableView.indexPathsForVisibleItems;
[self.myTableView reloadRowsAtIndexPaths:visibleCellIndex withRowAnimation:UITableViewRowAnimationFade];
[self.myTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:savedScrollPosition inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
The problem with reloading the visible cells, is: First, that's a workaround, not getting to the source of the problem. Second, it's not a fully functioning workaround, because the whole tableView scrolls all the way up for a second, then scroll back to position.
The reason why it was shrinking, is because, you have to implement the method of heightForRowAtIndexPath.
The only problem now, is that the tableView jumps up, then scrolls to position. Don't know why.
Does your target run only on iOS 8 and later? If yes, you can set self.tableView.rowHeight = UITableViewAutomaticDimension to enable Autolayout for your cells. Then, you also don't need to implement delegate tableView:heightForRowAtIndexPath:.
If you're already doing this, your problem probably lies in your custom cell. Maybe its constraints are not well defined? How do you initialize the cell's constraints?
Another idea is to trigger the layout pass manually in tableView:cellForRowAtIndexPath:. After the cell has been initialized and its text label values have been set, call:
[cell setNeedsLayout];
[cell layoutIfNeeded];

How do you force a specific row to be visible in UITableView when initially loading the table without any animation?

It has been suggested that one scroll to the desired row in viewWillAppear, but this does not work with iOS 7. I have only been able to make this work in iOS 7 in the viewDidAppear callback. Unfortunately, you see the desired row scroll into view. I don't want to see any scrolling, I simply want the row to be visible when loaded. Can anyone suggest the proper way to do this in iOS 7?
It probably did not work in viewWillAppear, because that table had no data at this point.
Add [tableView reloadData];and it should work.
Let me get this straight: you want your table view to show a certain row at the top when the view apperas? Yes?
If so, you want:
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
with your cell indexPath, UITableViewScrollPositionTop as scrollPosition and animated NO like so
[tableView scrollToRowAtIndexPath:myExampleindexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
If you know the cell index then it's as simple as:
[tableView setContentOffset:CGPointMake(cellLocation.x,cellLocation.y) animated:NO];
Call that just after you load your tableView data and it will scroll to your cell being on top. There are other options as well:
[tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:currentRow inSection:currentSection] animated:NO scrollPosition:UITableViewScrollPositionTop];
Use this code with whatever scrollPosition you would like and Apple takes care of the bounding to the table (whereas setting the scrolling position is all user defined, it could be out of the table's view).
EDIT:
You could surround your selecting code with a call to UIView setting no animations allowed. That has worked for me in the past with different things, but I have never tried it in viewDidLoad.
[UIView setAnimationsEnabled:NO];
//Scroll the tableview
[UIView setAnimationsEnabled:YES];

Little glitch when scrolling UICollectionView to bottom in viewDidAppear

I want my UICollectionView to automatically scroll to the bottom. I have this code in my viewDidAppear but when I run it, there is always an half-second pause between the view is showed and the collection view is scrolled to the bottom. I read other questions and didn't find an answer yet. Anyone has an idea?
- (void)viewDidAppear:(BOOL)animated{
NSInteger numbersOfItems = self.photoAssets.count - 1;
NSLog(#"%i", [self.collectionView numberOfItemsInSection:0]);
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:numbersOfItems inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
}
Are your cells fast to init? What may be happening is visible cells (at top) are getting created, then some additional ones have to get created, and it's slowing down the perceived time to scroll. You may want to reduce their complexity, and/or scroll to the items as soon as they are loaded into the collection view instead of waiting for viewDidAppear.
Have you tried calling this code in i.e viewDidLoad?
NSInteger numbersOfItems = self.photoAssets.count - 1;
NSLog(#"%i", [self.collectionView numberOfItemsInSection:0]);
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:numbersOfItems inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
Take a look at this guide for more information about the view load process

how to scroll tableview after inserting a row at the bottom correctly?

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.

Resources