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
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 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.
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);
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];
I have a tableview with custom cells with dynamic cell heights depending on the cell content.
My problem is the following, when I ask, programmatically, in the viewDidLoad, to scroll to a given position it works, except for the last row. Sometime the row appears but not fully, and sometimes it even does not appear. In both cases I have to scroll manually to see the row.
Here is the code :
[self.tableView reloadData];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:aRow inSection:aSection];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES ];
Is this a bug of iOS ? any workaround ?
As so far i came to know that,
All the operations used to before the view is shown on the screen are initialized in the viewDidLoad all the UI objects, data objects can be allocated and initialized in this method.
All the operations data modifications, UI modifications made to view need to be done in viewDidAppear. Or even some operations can be done in viewWillAppear.
So for your issue, the UITableView scrolling must be done after the table is loaded on & shown on screen i.e., in viewDidAppear.
Also note that viewDidAppear & viewWillAppear will be called each time view is shown to user, so if you want to scroll the table only for the first instance you can have a flag in your header indicating the instance.
[self.tableView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
NSIndexPath *rowIndexPath = [NSIndexPath indexPathForRow:3 inSection:0];
[self.tableView scrollToRowAtIndexPath:rowIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
});
I don't know exactly why, but I guess this approach works because when we add(???) rows and call [tableView reloadData] tableView has no time to update some internal counters (like row counter) and calling [tableView scrollToRowAtIndexPath ...] has no effect since there is no such row at that time (again, probably correct in case you add rows or set tableView's data for the first time). Calling
dispatch_async(dispatch_get_main_queue(), ^{
...
});
after [tableView reloadData] gives tableView enough time to update row counter and perform scroll to existing row.
Vishy's approach works just because it gives enough time but applicable only if you need to scroll exactly one time when screen is loaded. Moreover it requires ugly flag to check every time viewDid/WillAppear.