TableView sections become hidden after reloadSections - uitableview

My question is the following: there is a table consisting of headers. When you click on the header, it restarts after
[self.tableView reloadSections: [NSIndexSet indexSetWithIndex: section] withRowAnimation: UITableViewRowAnimationAutomatic];
Actually, in the earlier version (iOS 6.1.3 and lower), cell reloaded and everything is fine. On the iOS7 cells become hidden. What is it and how to solve it? And not only cell that is clicked, but the next standing(lower or above).
<MyHeaderCell: 0x146ea600; baseClass = UITableViewCell; frame = (0 0, 320 75); hidden = YES; autoresize = W; gestureRecognizers = <NSArray: 0x146e10c0>; layer = <CALayer: 0x146e95d0 »
Basically, it is one of those issues in the logs. It is clearly seen that cells become hidden.
Nevertheless, when I use [self.tableView reloadData]; everything is fine, but I need animation.

this is an old question, but I think I know what's causing this.
Is this a "static" cell in the sense that you're keeping a reference to it yourself? In that case, the problem is probably this:
When you do an animated reload, the table view will fade out existing cells while at the same time fading in the new cells. The problem is that when the "new" cell is the exact same cell as the old one, the same cell will both fade in and fade out at the same time! And in your case, the fade out animation takes precedence and the cell is hidden.
This if fixed by always reusing cells instead of using the same reference, however, I know that this is not always optimal.

Calling [tableView reloadData] right after [tableView reloadSections:withRowAnimation] fixes it without significantly affecting the animation.

Related

UICollectionViewCell gets hidden randomly

I have a UIView in which I am adding a UICollectionView to act as a banner view to look like a carousel. The Viewcontroller in which UIView is present, is part of pageviewcontroller.
PageVC --> UIViewController --> UIView --> UICollectionView.
Each VC has its own banner, so when the page is swiped, I reload the collectionView with the respective data and I am able to see it. Now if visit a page which I already visited, the collectionview cell disappears. The collectionview is visible but the cell is somehow hidden.
The collectionview cell is a customcell and I am not using any custom layout, I am using the default flowlayout. This is working fine in iOS8-devices.
Below is the debugger output,
When cell is visible:
CollectionViewCell: 0x7f9db8d9ffa0; baseClass = UICollectionViewCell; frame = (209 3.5; 209 96); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x7f9dbb5dde60>>
When invisible (you can see its hidden now) the dequed cell is now hidden, no clue why it is happening
CollectionViewCell: 0x7f9dbb5897e0; baseClass = UICollectionViewCell; frame = (0 3.5; 209 96); clipsToBounds = YES; hidden = YES; opaque = NO; layer = <CALayer: 0x7f9dbb590220>>
indexPathsForVisibleItems is returning nil.
I was calling reloadData from multiple places which caused the system to get confused (may be) especially I have written reloadData in my updateconstraints method and hence for each minor constraint change reloadData was getting called.
Check all places from where you call reloadData for collectionview if you're experiencing the same problem.
I've encountered the same problem.
After
checking if the UICollectionView reloadData was called in some thread other than the MainThread
checking if there is any cases about multi-calling reloadData or updateconstraints
without solving my problem, I realized that in some occasions CGSizeZero was returned for collectionView:layout:sizeForItemAtIndexPath:.
When avoiding the CGSizeZero result, everything works well.
Same problem.
Here's my situation:
My collection view has a custom layout. I use the default flow layout without any problem.
When I use my custom layout, whose cell width is a half screen width. When I scroll to the 5th cell, all cells became hidden without reason.
Finally I found out that when I set cell's size correctly in the layout class of the storyboard, the problem is resolved.
Hope this helps.

InsertRow Animation scrolls UITableView up

I try to implement a tableView where rows additional rows are inserted with an animation while the rows are visible. I update the array for the datasource and call:
[self.tableView insertRowsAtIndexPaths:addedIndexPaths withRowAnimation:(UITableViewRowAnimationMiddle)];
I want the tableview to not scroll at all and the rows to be inserted on screen (no over the visible are).
In some section especially the last section of the tableview the cells are inserted the tableview scrolls to completely different section.
I tried different row animation, calling layoutIfNeeded inside of and beginUpdate / endUpdate block, set the correct content offset inside of the beginUpdate / endUpdate block or reload the entire section.
Nothing works. The scrollview always scrolls up.
The cells size them selves using autolayout-constraints. On the top half of the tableview the cells are inserted as I expected.
How can I fix the content Offset while cells are inserted with an animation?
How can I debug that animation?
I faced same issue and after trying a lot found solution that if I remove section header height as UITableViewAutomaticDimension then adding and removing rows from UITableView works perfectly and table view also fixed at same position.
tableView.sectionHeaderHeight = 10;
tableView.estimatedSectionHeaderHeight = UITableViewAutomaticDimension;
remove above 2 lines if added.
tableView.estimatedRowHeight = 15;
tableView.rowHeight = UITableViewAutomaticDimension;*
Row with Automatic Dimension will not create any issues.
But section header view with Automatic Dimension will create issue.
Thanks.
This is a know bug with the UITableViews, it's been there quite a long time and hasn't been fix (by the looks of it, it never will).
The root of this is because your cells are of dynamic sizes and (I'm assuming) you are using the default 'UITableViewAutomaticDimension'. To fix this you need to drop the use of 'UITableViewAutomaticDimension' and calculate the size of each cell. This should help with the bounciness of the table view but probably wont be perfect.
Another way is to rely on CATransaction to lock and keep the table view position after the insert.
It should look something like this (probably, I didn't test it):
CGFloat offset = self.tableView.contentSize.height - self.tableView.contentOffset.y;
[CATransaction begin];
[CATransaction setDisableActions:YES];
[CATransaction setCompletionBlock: ^{
// Code to be executed upon completion
}];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths: indexPaths
withRowAnimation: UITableViewRowAnimationAutomatic];
self.tableView.contentOffset = CGPointMake(0,
self.tableView.contentSize.height - bottomOffset);
[self.tableView endUpdates];
[CATransaction commit];

Prevent UITableView out of cell bound views from clipping on insertRowsAtIndexPaths:

This video shows how cells with views extending out of the cell area get clipped momentarily when new cells are being inserted:
https://dl.dropboxusercontent.com/u/22105205/CellClipping.mov
This simple project clearly shows the problem and can be used for quick prototyping:
https://github.com/AndresCanella/iOSInsertCellClippingExample.git
This clipping only occurs when the table is mutated.
Cells are clear.
Cells display correctly when not mutating.
Possibly some sort of optimization that only uses pix from within the cell area for animation?
Everything is setup correctly, stable, and works as expected, we are not even using specific cell data for this example:
[tableView beginUpdates];
self.cells++;
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
[tableView endUpdates];
Update:
Response from DTS:
I’m afraid you are not going to be able to directly affect the insertion animation behavior when calling “insertRowsAtIndexPaths”, regardless of the kind of “UITableViewRowAnimation” you are using. Cell content conventionally don’t overlap like that. UITableView is simply honoring the cell’s bounds (not it’s extended or overlapping content), when performing its animation block of each cell.
My comment:
I've been told by DTS that things can be done plenty of times and I've always found a workaround. So now I'm looking for a work around.
Apple Bug Report # 17986466
It looks to me like the views you wish to not be clipped exceed the bounds of the cells themselves. To me, that says that these should be subviews of the table view instead of the cell.
If that is indeed the case, you may wish to use a plain UIScrollView instead of a UITableView, and animate the the views below the one being inserted downwards.
In my experience you should try to keep the cells with a non-clear/transparent background and set to clip subviews if you want to avoid weird layouts and animation glitches.
The way the cells view hierarchy is set inside the table view and how animations are made is internal to Apple implementation and prone to change without notice in future releases.
Table views are good at displaying tons of rows and reusing views, things that maybe your view is not really using. If your desired layout does not require several screens of scrollable content maybe you should try to create your own custom UIScrollView-based view or search for one among the many open source libraries. You would have complete control of animations and add custom behaviors.
I understand that this is a complete hack - but it does fix the clipping.
For granular animations - check out PRTWeen.
https://github.com/jdp-global/MWDatePicker
I guess you considered toggling 2 transparent background images (red and green) (640px x100px) on current cell and previous cell? It may work using a fade in animation on insert.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.cells++;
[self.tb reloadData];
[self performSelector:#selector(fancyAnimate:) withObject:indexPath afterDelay:0];
}
-(void)fancyAnimate:(NSIndexPath*)path{
NSIndexPath *idx = [NSIndexPath indexPathForRow:path.row inSection:path.section] ;
UITableViewCell *cell = (UITableViewCell*)[self.tb cellForRowAtIndexPath:idx];
CGRect frame = cell.frame;
frame.origin.x = 320;
cell.frame = frame;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
frame.origin.x = 0;
cell.frame = frame;
[UIView commitAnimations];
}

deleteRowsAtIndexPaths Overlap Issue

Trying to be ios7-esque, I am inserting a UIPickerView into a UITableView when tapping on a cell in the table. This works fine and animates nicely. However, there is an issue when I retract the cell by calling deleteRowsAtIndexPaths.
I am experiencing a "bleed"/overlap where the picker is hiding one of the cells further down in the table view. See the screenshots.
I'm not doing anything super custom, so I wonder if this is an iOS7 bug. All cells have solid background colors (white).
Any help would be greatly appreciated. Thanks!
Tapping the top row
This is mid animation when retracting. Notice the overlap and the picker bleeding out over the cell at the bottom
I'm not sure why, but it looks to me like the picker cell is covering the cell below "Choose Product". If this is indeed the case, one workaround would be to explicitly set the z-order of your cells, placing the picker cell under all others:
#import <QuartzCore/QuartzCore.h>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ...;// your logic for getting a cell
BOOL isPickerCell = ...;// your logic for identifying if this is the picker cell
cell.layer.zPosition = isPickerCell ? 0 : 1;
}
If the picker is near the bottom of the table, it could still show through below the last cell since there's nothing there to cover it. For example, if "Choose Product" were the last cell. You can work around this by inserting blank cell(s) at the bottom. This is a general problem with having cells of varying height.
After struggling with this problem, I realised that Apple's calendar application has the same issue
However, they minimise the side effects by inserting the row with a .fade animation.
tableView.insertRows(at: indexes, with: .fade)
I had similar issues (iOS 8, iPhone 6 simulator)
In my case, I had a custom cell containing a DatePicker being inserted/deleted either between Right Detail style cells or between a Right Detail style cell and the section footer, which worked as expected.
[self.table beginUpdates];
if (isVisible) {
[self.table insertRowsAtIndexPaths:#[index]
withRowAnimation:UITableViewRowAnimationMiddle];
} else {
[self.table deleteRowsAtIndexPaths:#[index]
withRowAnimation:UITableViewRowAnimationMiddle];
}
[self.table endUpdates];
But I also had Right Detail style cell being inserted/deleted between a Right Detail style cell and the end of section, which did not work as expected using the same code. The appearing/disappearing cell was visible on top of/through the cell above, and the cell moved twice as far as it should have. In the image below, People is appearing below Privacy, mid-animation.
However, I noticed that when the beginUpdates/endUpdates were commented out, the cell only moved about half a cell height instead of twice a cell height which meant that it looked much improved.
I also tried setting the zPosition which appeared to lessen the visibility when the cells overlapped.
// [self.table beginUpdates];
if (isVisible) {
[self.table insertRowsAtIndexPaths:#[index]
withRowAnimation:UITableViewRowAnimationMiddle];
} else {
cell = [self.table cellForRowAtIndexPath:peopleIndex];
cell.layer.zPosition = -1;
[self.table deleteRowsAtIndexPaths:#[peopleIndex]
withRowAnimation:UITableViewRowAnimationMiddle];
}
// [self.table endUpdates];

UITableView: Juxtaposing row, header, and footer insertions/deletions

Consider a very simple UITableView with one of two states.
First state:
One (overall) table footer
One section containing two rows, a section header, and a section footer
Second state:
No table footer
One section containing four rows and no section header/footer
In both cases, each row is essentially one of four possible UITableViewCell objects, each containing its own UITextField. We don't even bother with reuse or caching, since we're only dealing with four known cells in this case. They've been created in an accompanying XIB, so we already have them all wired up and ready to go.
Now consider we want to toggle between the two states.
Sounds easy enough. Let's suppose our view controller's right bar button item provides the toggling support. We'll also track the current state with an ivar and enumeration.
To be explicit for a sec, here's how one might go from state 1 to 2. (Presume we handle the bar button item's title as well.) In short, we want to clear out our table's footer view, then insert the third and fourth rows. We batch this inside an update block like so:
// Brute forced references to the third and fourth rows in section 0
NSUInteger row02[] = {0, 2};
NSUInteger row03[] = {0, 3};
[self.tableView beginUpdates];
state = tableStateTwo; // 'internal' iVar, not a property
self.tableView.tableFooterView = nil;
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObjects:
[NSIndexPath indexPathWithIndexes:row02 length:2],
[NSIndexPath indexPathWithIndexes:row03 length:2], nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
For the reverse, we want to reassign the table footer view (which, like the cells, is in the XIB ready and waiting), and remove the last two rows:
// Use row02 and row03 from earlier snippet
[self.tableView beginUpdates];
state = tableStateOne;
self.tableView.tableFooterView = theTableFooterView;
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:
[NSIndexPath indexPathWithIndexes:row02 length:2],
[NSIndexPath indexPathWithIndexes:row03 length:2], nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
Now, when the table asks for rows, it's very cut and dry. The first two cells are the same in both cases. Only the last two appear/disappear depending on the state. The state ivar is consulted when the Table View asks for things like number of rows in a section, height for header/footer in a section, or view for header/footer in a section.
This last bit is also where I'm running into trouble.
Using the above logic, section 0's header/footer does not disappear. Specifically, the footer stays below the inserted rows, but the header now overlays the topmost row. If we switch back to state one, the section footer is removed, but the section header remains.
How about using [self.tableView reloadData] then? Sure, why not. We take care not to use it inside the update block, per Apple's advisement, and simply add it after endUpdates.
This time, good news! The section 0 header/footer disappears. :)
However ...
Toggling back to state one results in a most exquisite mess! The section 0 header returns, only to overlay the first row once again (instead of appear above it). The section 0 footer is placed below the last row just fine, but the overall table footer - now reinstated - overlays the section footer. Waaaaaah … now what?
Just to be sure, let's toggle back to state two again. Yep, that looks fine. Coming back to state one? Yecccch.
I also tried sprinkling in a few other stunts like using reloadSections:withRowAnimation:, but that only serves to make things worse.
NSRange range = {0, 1};
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
...
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationFade];
Case in point: If we invoke reloadSections... just before the end of the update block, changing to state two hides the first two rows from view, even though the space they would otherwise occupy remains. Switching back to state one returns section 0's header/footer to normal, but those first two rows remain invisible.
Case two: Moving reloadSections... to just after the update block but before reloadData results in all rows becoming invisible! (I refer to the row as being invisible because, during tracing, tableView:cellForRowAtIndexPath: is returning bona-fide cell objects for those rows.)
Case three: Moving reloadSections... after tableView:cellForRowAtIndexPath: brings us a bit closer, but the section 0 header/footer never returns when switching back to state one.
Hmm. Perhaps it's a faux pas using both reloadSections... and reloadData, based on what I'm seeing at trace-time, which brings us to:
Case four: Replacing reloadData with reloadSections... outright. All cells in state two disappear. All cells in state one remain missing as well (though the space is kept).
So much for that theory. :)
Tracing through the code, the cell and view objects, as well as the section heights, are all where they should be at the opportune times. They just aren't rendering sanely. (Update: The view heights are NOT the same, but I didn't change them either! See my posted answer for more info.)
So, how to crack this case? Clues welcome/appreciated!
I had the same issue today.
After a while I came up with the idea that there is an error in the way everything is draw. In the appearance of table view cells method I removed the condition clause like this, everything worked :
//if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
//}
I'll have a closer look at the problem as the code is not optimized anymore now. But it works for the time being.
I'm putting this in the answer section because it helps (partially?) explain what I'm seeing. It just doesn't explain why yet. :)
When I first assign a view to the table section header (in response to tableView:viewForHeaderInSection:) it's set just as I defined it in the XIB:
<UIView: 0x376620; frame = (0 0; 320 50); autoresize = RM+BM;
layer = <CALayer: 0x376720>>
Some time after we change the cells around and start responding to tableView:viewForHeaderInSection: with nil for section 0, said view gets some CABasicAnimation love (the table is about to animate, after all) ... and the view frame and autoresize params change!
<UIView: 0x376620; frame = (0 0; 320 10); autoresize = W;
animations = { position=<CABasicAnimation: 0x360b30>;
bounds=<CABasicAnimation: 0x360a30>; }; layer = <CALayer: 0x376720>>
If we then switch BACK to the first state, and return that very same view as the section header once again, we see a bit of debris at assign-time:
<UIView: 0x376620; frame = (0 0; 320 10); autoresize = W;
layer = <CALayer: 0x376720>>
Yep. The frame and autoresize are not reset!
So it would seem we have run in to an unintentional side-effect when effectively removing a view from a table's section header.
My knee-jerk reaction: "If you're going to mess with my UIView, please put things back the way you found them!" At this point I don't know if this is a realistic expectation, but it's the first one that comes to mind.
To mitigate, I suppose I'll have to reset the frame and autoresize each time. Hmm ... seems a bit messy, y'think? Perhaps there's a better way, or I'm committing a faux pas elsewhere.
Then again, this frame/autoresize adjustment doesn't seem to pose a problem for the overall table header/footer view, even when they're removed and later re-added. Just the section header/footer views. (Wait, a correction: I can't tell if it affects the table footer view because there's nothing below it to collide with.)
For now, I've embedded another view within each header/footer view. This view is identical in form to the parent view, but it doesn't get changed at animation-time. Then it's just a matter of invoking something akin to this for each header/footer view affected by the change in state:
- (void)fixViewAnimationCruft:(UIView *)theView {
UIView *subview = [[theView subviews] objectAtIndex:0];
theView.frame = subview.frame;
theView.autoresizingMask = subview.autoresizingMask;
}
(Not the most original method name, but it will do for now.)

Resources