Implementing estimatedHeightForRowAtIndexPath: causes the tableView to scroll down while reloading - ios

I've implemented 'infinite scrolling' on one of my projects and I was playing around with the new estimatedHeightForRowAtIndexPath: delegate method. Once I implemented that delegate method, my tableView jumps (a.k.a scroll to the bottom) whenever i call reloadData (which happens when i add a new set of rows.)
Without that method, my tableView stays in place and it adds the additional rows to the bottom of the tableView without any scrolling.
I'm calling the [tableView reloadData] and not the other methods (insertRowsAtIndexPaths:). I don't call the beginUpdates or endUpdates since I'm reloading the whole table.
Has anyone experienced this ? I

So here's what i did to reduce the jumping while reloading the tableView.
In the estimatedHeightForRowAtIndexPath: I started returning a better estimated height. The more accurate the height is, lesser the jumping while adding new rows to the bottom.
I also started caching the calculated height of the cell from heightForRowAtIndexPath: in a dictionary and return the cached value the next time it calls the estimatedHeightForRowAtIndexPath:.
Hope this helps anyone who encounters this issue.

I filed a BugReport quite a while ago (16472265) and today got the response that it's fixed in iOS8. Tried it out and it works now as expected. No more jumping of the tableview! Wohoooo

Check your condition in this scrollView Delagete method,
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

Related

UITableView how to continue scrolling after reload

I have a UITableView in my application. When the table is scrolled almost to the end, I download more data and reload the table.
If the tableView was scrolling, at the time I call [tableView reloadData]; scrolling stops. How can I achieve effect of not stopping the scroll meanwhile reloadData? I think I need to somehow save scrolling speed and then restore it, but how to do this?
P.D. I really searched this question before asking.
I thing, this method (insertRowsAtIndexPaths:withRowAnimation:) is the key.
There is a use case and a nice tip described on SO: the use case and the tip.
Since UITableView is subclass of UIScrollView, you can use UIScrollViewDelegate's scrollViewWillEndDragging:withVelocity:targetContentOffset:
method to determine how far it is supposed to scroll and then after table reload call setContentOffset:animated: with that target offset (or beginning/ending of tableview if it becomes smaller) to simulate continued scrolling
EDIT: since your targetContentOffset is probably going to be CGRectZero, you will have to recalculate it somehow using velocity from the same method
if i am not wrong you are looking for a function call Lazy load. I can recommend you to search SVPullToRefresh
here!

Self Sizing Cells make UITableView jump

I've got a UITableView, each cell has an image and two labels, as you can see on the first picture
So I am trying ti use self-sizing cells and everything is great, except 2 things:
1) First and Second cell don't show content properly, but after I scroll down and then return to them everything is ok. I tried to use [tableview reloadData] in viewDidAppear, but it doesn't help. Watch the labels at first they don't fit. You can watch it on this video https://www.youtube.com/watch?v=I9DGBl1c5vg
Look at the labels on the first cell.
2) It's a tough one. I'm scrolling the table and everything is great, but the problem happens, when I select the row. It's pushing me to detail view, but when I press "back" and return to master view, the tableview jumps,so I'm coming back not to the selected row, also if I scroll the table it doesn't scroll smooth, but jumps. Here are the links for two videos, first one shows that scroll is smooth https://www.youtube.com/watch?v=9wAICcZwfO4 if i don't go to detail view, and the second shows the jumping problem https://www.youtube.com/watch?v=fRcQ3Za1wDM .
It's absolutely sure connected with self sizing cells, because if I don't use it, none of this problem happens.
Okay, I solved both of the problems.
Question 1
I've added these two lines
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath before return cell
Question 2
The solution appeared to be very simple. I've just needed to implement viewWillDissapear and reload data in it, so the code looks like this
- (void)viewWillDisappear:(BOOL)animated;{
[super viewWillDisappear:animated];
[self.tableView reloadData];
}
Solution for me was simple, if it doesn't work for someone, here I found some useful links.
1) UITableView layout messing up on push segue and return. (iOS 8, Xcode beta 5, Swift)
2) http://useyourloaf.com/blog/2014/08/07/self-sizing-table-view-cells.html
Unfortunately I believe both of the questions that you ask about are IOS bugs.
Question (1)
A easy fix is suggested by this blog.
When the table view is first displayed, you may find some of the cells are not sized properly. But when you scroll the table view, the new cells are displayed with correct row height. To workaround this issue, you can force a reload after the view appears:
Simply add the following to your viewDidAppear method. I have tested it and it works very well.
[self.tableView reloadData];
Question (2)
This second question is a duplicate of this following question:
IOS 8 UITableView self-sizing cells jump/shift visually when a new view controller is pushed
A workaround is suggested by the author of the question himself, which seems okay. However, I have not tried out this one yet.
Okay, I solved it by caching my cell heights in sizeThatFits, and returning that value for estimated cell heights within the delegate. Works beautifully.
Feel free to head over to that question for other proposed solutions.

UICollectionView stops loading cells after moveItemAtIndexPath:toIndexPath:

I have a UICollectionView with UICollectionViewFlowLayout which contains many items. I want to reorder a cell due to the user input.
[self.collectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
After execution of this code, the collection view doesn't update it's contents when scrolling. It doesn't show next cells. It doesn't even call cellForItemAtIndexPath: but it's content size is correct (the layout "updates" it's state).
Therefore we can only see the cells which were distributed on the collection view before the reorder.
Any clues what might be the issue ? Are there any known workarounds for this issue? The app is iOS 8 only and the collection view is used with the UICollectionViewFlowLayout.
EDIT: When I call [self.collectionView reloadData]; the datasource is not even asked for anything, too.
The issue was with the UICollectionViewCell. High level description is that the cell is sent a method actionForKey from my code during the move animation. It caused the cell to stay in internal being animated state. I couldn't find reasonable workaround and refactored some bits in order to avoid actionForKey call. Now everything works as expected.

UITableView insertRows without locking main thread

I'm trying to implement infinite scroll like the Facebook app has.
It appears that Facebook is somehow inserting rows or reloading the table view without stopping the current scroll.
Does anyone know how they're achieving this?
When I call insert rows or reload data during a scroll the scroll is stopped dead. Any help would be appreciated.
Not much code to provide just typical UITableView functions.
[self.tableView insertRowsAtIndexPaths:self.paths withRowAnimation:UITableViewRowAnimationNone];
or
[self.tableView reloadData];
self.paths being the paths recently added via the next page request.
I've also added the following method
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;
Which is working correctly now with reloadData being called. Unfortunately even while this speeds up the addition of cells it still locks the current scroll.
Okay so, as far as the stopping of the currently active scroll is concerned the problem came down to a single line of code. And the solution was just as small.
My TableView has a refresh control on it. In the method in which I receive the next set of data for the TableView I would call the following.
[refreshControl endRefreshing];
My assumption was if it wasn't refreshing that endRefreshing would simply do nothing.
Well... Apparently that's not the case. When you call endRefreshing on a refresh control that isn't refreshing it'll stop the tableview scroll. My fix is as follows.
if(refreshControl.isRefreshing){
[refreshControl endRefreshing];
}
Now the scroll continues after the next load albeit with some delay. That delay of course is easier to debug.
That's just the about of overhead involved in calculating the height of the cells being added which is called on all cells when reload is called unless you have
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;
So now that I have everything sorted out it's time to create an efficient way to calculate a reasonably close estimate height.

How to notice if a UITableViewCell has left the visible area?

I'm stuck with the problem that I want to know in my UITableView if a specific UITableViewCell, let's say the first, is still visible or already off the visible area.
I would also be ok to know if the cell of interest is now beeing reused at an other indexPath of the table.
One of my - later and frustrated approaches - was to have a thread that knows the first cell object and frequently pings it to check if a value I did set in the cell changed. Obviously a not so good solution.
Andy ideas how to do this right?
Remember that UITableView is UIScrollView subclass and its delegate also confirms to UIScrollViewDelegate protocol as well.
So in your table delegate you can implement scrollViewDidScroll: method and check contentOffset - if it's more then first cell height then first cell is not visible. You can also get the array off all currently visible cells using -visibleCells method, but I think knowing contentOffset should be enough.

Resources