Folks,
I am getting a weird issue while applying scroll to load more to my UITableView. I added a YouTube video also for reference. At scrolling to bottom, API hits server and gets next 5 records. At this time I am not reloading the table, neither I want too. I am simply adding more rows to the table. The code is below:
// adding new rows rather than reloading the table
if(loadMore)
{
NSMutableArray *indexPaths = [NSMutableArray array];
for (NSUInteger i = 0; i < arr.count; i++)
{
[indexPaths addObject:[NSIndexPath indexPathForRow:arr.count+i inSection:0]]; // adding max 5 new indexPaths
}
[self.tblData beginUpdates];
[self.tblData insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; // insert with fade animation
[arrData addObjectsFromArray:arr]; // adding new data to existing array
[self.tblData endUpdates];
}
Now see the video I uploaded. On 1st load more it works like charm. You can see it at 00:07. It adds 5 more rows at the bottom with fade animation.
But after 1st load more, for next 5 load more it brings 5 more records from the server and adds them to the table but also it autoscrolls at the same index, see video at 00:15, 00:23, 00:30 an so on.
You can see it works good on 1st load more (at 00:07), but never after that even all the records are coming good.
After that I changed simulator animation to Slow and you can see that after 01:01
Can anyone suggest me why it is happening? Somewhere I found it is due to AutoLayout, then i made another storyboard which has disabled autolayout but problem still occurs.
Related
I've created a set of classes to simplify managing table views. One of the things they do is create a diff for a section based on what data is updated and then apply the correct insertions, deletions and moves.
This is all working great for most of my app, except for two uncooperative table views. Call them table A and table B. I've found a fix that works for each individually, but I can't fix both at the same time. I've checked that the diffs are being correctly generated, but for some reason I get different animations in each.
Both table A and table B are simply inserting rows. There are no deletions or moves.
In the below code BREAK_TABLE_A causes:
Table A - All rows in section=0 row>=1 (not row=0) to shift to the bottom while re-animating in from the top. Very ugly
Table B - A nice insertion animation
BREAK_TABLE_B causes:
Table A - A nice insertion animation
Table B - No animation. Just like reloadData
Ultimately, I would actually prefer the method in BREAK_TABLE_B because this allows me to keep the same cells for the same data, which means I can perform animations on those individual cells using configureCell:.
The code is relatively straightforward:
UITableViewRowAnimation animation = UITableViewRowAnimationFade;
...
[self.tableView beginUpdates];
NSMutableSet *insertedPaths...;
NSMutableSet *deletedPaths...;
NSMutableSet *refreshPaths...; // Will contain any cell that was present before and after the update.
[self.tableView moveRowsAtIndexPaths:... // Not executed in these examples
...
[self.tableView insertRowsAtIndexPaths:insertedPaths.allObjects withRowAnimation:animation];
[self.tableView deleteRowsAtIndexPaths:deletedPaths.allObjects withRowAnimation:animation];
if (BREAK_TABLE_A){
[self.tableView reloadRowsAtIndexPaths:refreshPaths.allObjects withRowAnimation:UITableViewRowAnimationNone];
} else { //BREAK_TABLE_B - I would prefer this solution if possible
for (NSIndexPath *path in refreshPaths){
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:path];
if (cell){
NSUInteger index = [newSection.rows indexOfObjectPassingTest:^BOOL(LQTableViewRow * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return [obj.rowIdentifier isEqual:oldSection.rows[path.row].rowIdentifier];
}];
LQTableViewRow *row = newSection.rows[index];
[self configureCell:cell section:newSection row:row];
}
}
}
... update the actual data source ...
[self.tableView endUpdates];
In order to fix this, I've tried wrapping the above code in [CATransaction begin/commit/flush] as well as [UIView begin/commitAnimations] blocks. CATransaction freezes the app with no errors (probably waiting for some other animation) when using table B. UIView causes both views (and all others in the app) to animate incorrectly (cells fade in and animate from bottom to top of their final positions, rather than from the top to the bottom of the tableview).
I've also tried to load the views with dummy data such that they are as similar to each other as possible with the same weird results.
Well, it turns out that the crash when using the CATransaction led me to the answer. I had accidentally been doing [CATransaction begin] twice in one of my cells, instead of committing the transaction. Fixing that fixed the animations.
I have a UICollectionView that is backed by a web service. I initially load 100 items from the service into the collection view. When the user reaches the bottom of the view, I dyanmically load the next "page" of content.
This is working, but my collectionView flashes and re-draws the entire view from the top. I am trying to get it to just begin drawing the new content at the bottom of the view.
Code for adding new content to the collectionview:
- (void)insertIntoCollectionView: (int) itemCnt {
// only do the batchupdate for NON-initial load
if(curPage != 0) {
// Add items to main photos array, and update collectionview using batchUpdate
[self.collectionView performBatchUpdates:^{
// Get starting size before update
int resultsSize = itemCnt;
// Create a new array to hold the indexpath for the inserted data
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
// Add indexpath objects to the new array to be added to the collection view
for (int i = resultsSize; i < resultsSize + itemCnt; i++) {
[arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
// Update the collectionview
[self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
}
completion:nil];
} else {
[self.collectionView reloadData];
}
curPage++;
Anything obvious? It functionally works, but looks pretty bad with all of the flashing and re-drawing.
OK, managed to fix it myself. I was using the incorrect values for the resultSize variable. The for loop should start at the end of the current array count, and loop through the number of item that we added, thus adding them at the end. I was starting at the beginning of the array index 1 when building the arrayWithIndexPaths, causing it to re-draw the whole view.
I'm trying to implement an infinite UITableView. I load some information from the server in chunks and I want to dynamically add this information to the tableview when user scrolls the table.The problem I'm facing : I have an initial tableview with 40 elements and then in scrollViewDidScroll I check if last visible row's indexPath == [myData count] - 20 (so when user has already scrolled half of the list I load next content) I load next 40 elements of content from the server and only when last visible row's indexPath == [myData count] -1 I actually reload the tableview. So as you see i make some eager caching in order to make tableview reloading faster.Everything work perfectly but when I begin to scroll my table really fast data stopped loading. It seems that this check: last visible row's indexPath == [myData count] - 20 has actually no chance to implement because of fast scrolling.I can't see any other obvious reasons for this. So has someone some answer for this weird problem?
P.S. Or may be I should try reloadRowsAtIndexPaths instead of reloading the whole table?
Maybe with this delegate you have a chance to do something in advance. There is also possible to reload only the visible rows which it's better than reload data.
-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
NSArray *paths = [self.tableView indexPathsForVisibleRows];
int tmprow = [[paths objectAtIndex:0] row];
}
I have a bug that I've spent way too much time on myself. Basically, I'm creating an "accordion" Table View based off Apple docs:
https://developer.apple.com/library/ios/#samplecode/TableViewUpdates/Listings/ReadMe_txt.html#//apple_ref/doc/uid/DTS40010139-ReadMe_txt-DontLinkElementID_3
The way the code works is that if you tap on a Table View Header, code gets called to add rows to that section.
The only thing that's different is the fact that it's for iOS 5, so for my header views I'm not using the reusable header view stuff.
Basically, every time I click on a header to show my rows, the cells that are added/removed via [tableView beginUpdates]/[tableView endUpdates] turn black. HOWEVER, if I scroll the table view after then it goes back to being normal.
Pictures showing this in action: http://imgur.com/a/B3lMu
Some code of how I remove cells :
// GET THE SECTION INFO
SWDeviceSectionInfo *deviceSectionInfo = [self.deviceTableSectionInfo objectAtIndex:section];
// "CLOSE" THE SECTION
deviceSectionInfo.open = NO;
// CREATE INDEX PATHS FOR ROWS THAT NEED TO BE DELETED
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
for (int i=0;i<deviceSectionInfo.rowInfo.count;i++) {
// CREATE INDEX PATHS FOR EACH ROW
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:section]];
}
// APPLY THE ROW INSERTIONS
[self.deviceTableView beginUpdates];
[self.deviceTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationBottom];
[self.deviceTableView endUpdates];
Some extra notes:
The background for the cells after scrolling table view is that of the Table View (I can't set the BG for cells). The Yellow view is a view on TOP of the cell background that's supposed to be green.
If I click on the iOS simulator debug "color" then again, the black cells disappear.
I've tried changing "highlighted", "selected" color to random colors and nothing changes. Remains a black cell bug.
I've triple checked the code for stuff like adding cells to the wrong section, all of that is seemingly correct.
The bug also occurred when I was using the default white cells (no custom nib).
The problem was that I had a view on top of the table.
To be more specific, the view had clear color background and shadows added to it so it would form an "inner shadow."
That view SOMEHOW caused the table to render completely wrong when it got updated.
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.