Why do UITableView zero height cells leave space gaps? - ios

My Objective-C Catalyst application has a table view which displays hierarchical data in the form of Groups within Groups and Notes and Groups within Groups. The Groups have a disclosure control that when toggled either "opens" the group to display its contents, or "closes" the group to hide its contents.
The problem, as illustrated by the following two screenshots, is that when a Group containing more than a few items is "closed", a noticeable vertical gap space is shown below the "closed" group. The height of the gap space is proportional to the number of items within the "closed" group. When the "closed" group is toggled "open", the gap space disappears––returns to normal cell spacing. This problem exists in both the iOS and macOS builds of the Catalyst app.
The tableView cells are Style Custom cells. The display of "open" or "closed" groups is controlled by varying the cell height based on whether an item is visible or not. If an item is buried within a "closed" Group, the cell's height is set to zero, otherwise the cell's height is set normally. This is accomplished in the tableView delegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
…
…
…
// Node is hidden if an ancestor is collapsed.
if ([model isNodeWithinCollapsedAncestor:node]) {
return 0;
} else {
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
I have tried numerous modifications in attempt to eliminate these gaps, without success. Among these are:
Varied UITableView attributes.
Varied UITableViewCell attributes.
Set cell.contentView.layoutMargins.
Set cell.contentView.hidden = YES (even worse gaps since it leaves whitespace for every hidden cell).
Varied cell constraints.
Replaced Style Custom cells with Style Basic cells. (Since Basic cells still had gaps, I believe it rules out my Custom cells as the problem.)
Nothing seems to prevent the gap spaces. It appears to me that there is some residual height to a zero height cell, perhaps as little as a few points, but I have had no success in getting rid of the space gaps.

Related

UITableView cell is visible through other cells during animation

I do have a “collapsible cells” in my UITableView. When user taps on a button within cell - it’s internal constraints are recalculated and cell changes it’s size - either to a smaller one or to a bigger one (collapse/expand). My problem is that when one of the last cells is expanded and then user collapsed it - this moving (and collapsing) cell is visible through other cells and between section’s headers - my reputation here is not high enough to show you a screenshot, unfortunately. Cell is built within .xib file, I tried to set “clip to bounds” parameter to TRUE to all views in the contentView of this cell - no effect. Does anybody have an idea - which direction should I look at?

IOS/Objective-C: Reusing Cell Throws off Autolayout of Different Size Elements in TableviewCell

I am displaying feed items in a tableview. Depending on the item, the cell must be larger or smaller--for example larger to accomodate a photo or larger still if it references another feed item such as an article and I have to display the item referenced.
In most cases, I've used autolayout to get the cells to auto expand or contract based on the content. However, in a couple cases, this was proving next to impossible so I reset the value of a constraint in code. For example, if there is no photo in one case, I shrink the height of the cell in code as the cell was not shrinking on its own.
This works fine when the page loads. But when you scroll and reuse cells, it leads to weird effects such as the cell expanding vertically or lines being pushed into one another.
Some questions on SO suggest using the method:
-(void) tableView:(UITableView *)tableView didEndDisplayingCell:(IDFeedCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
//Undo changes here
}
The problem is I don't know how to undo changes. For example, if I made a cell smaller by setting
self.height.constant = 100;
Should I set it back to the constant value in storyboard?
What value can I set it to when I don't know what the next item to resuse the cell will need in the way of dimensions.
Thanks for any suggestions. BTW, autolayout shows no issues.

Resize UITableView to fit all cells

I am using a UITableView within an auto-sized UITableViewCell to display a variable, but small number of items. I want the nested UITableView to expand to display all of its rows. (I know this may not be the best way to do things, so please bear with me on the premise.)
The problem I'm having is that the UITableView does not have an intrinsic size based on the size of all of its cells. Is there any way to force all of the cells to load an then compute an intrinsic size? (Or some other way to have a full-sized nested UITableView.)
Thanks!
There is a method called
- (void)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
where you define the height of each cell. I believe the default is 44. You can just take those values and then multiply them by the number of cells you're populating and that will be the height your UITableView should be.

UITableView powered by FetchedResultsController with UITableViewAutomaticDimension - Cells move when table is reloaded

Current set up:
TableView with automatically calculated heights:
self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension;
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 152.0;
self.tableView.estimatedSectionHeaderHeight = 50.0;
Whenever the fetched results controller updates its data the tableview is reloaded:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView reloadData];
}
The cell is configured using a Xib. The first label is pinned to the top of the cell, each following label is pinned to the top of the label above it and the label at the bottom is pinned to the bottom of the cell.
The Issue:
Each time i set a "Favourite" property on an item in the table view, the fetched results controller is fired to reload the table and the scroll position is changed. It is this change in the scroll position that i am trying to fix.
Additional Info
If i use fixed cell heights it resolves the issue BUT i require UITableViewAutomaticDimension because the first label can wrap over two lines and the remaining labels may or may not be present.
Example
Note - As i select the Fav button it sets the fav property in Core data and reloads the table. Why is the table jumping around?
It happens because of the following sequence:
UITableView initialized and showing 5 cells. Height of each of that cells is known to UITableView. It asks its delegate for exact height before displaying each cell by calling a method -tableView:heightForRowAtIndexPath:.
UITableView scrolled exactly 3 cells from top. Heights of this cells are known to be exactly [60, 70, 90] = 220 summarily. UITableView's contentOffset.y is now 220.
UITableView gets reloaded. It purges all its knowledge about cells. It now still knows its contentOffset.y which is 220.
UITableView asking its data source about general metrics - number of sections and number of rows in each section.
UITableView now beginning to fill its contents. First it needs to know size of its contents to correctly size and position its scroll indicators. It also needs to know which objects - table header, section headers, rows, section footers and table footer - it should display according to its current bounds, which position is also represented by contentOffset. To begin placing that visible objects it first needs to skip objects that falls in invisible vertical range of [0…220].
If you haven't provided values for any of estimated… properties and haven't implemented any of tableViewController:estimated…methods then UITableView asks its delegate about exact height of headers, footers and rows by calling appropriate delegate methods such as -tableView:heightForRowAtIndexPath:. And if your delegate reports the same number of objects and the same heights for them as before reload, then you will not see any visual changes to position and size of any table elements. Downside of this "strait" behavior became obvious when your table should display large number of rows, lets say 50000. UITableView asks its delegate about height of each of this 50000 rows, and you have to calculate it yourself by measuring your text for each corresponding object, or when using UITableViewAutomaticDimension UITableView doing the same measuring itself, asking its delegate for cells filled with text. Believe me, it's slow. Each reload will cause a few seconds of interface freeze.
If you have supplied UITableView with estimated heights, then it will ask its delegate only for heights of currently visible objects. Objects in vertical range of [0…220] are counted by using values provided in estimatedRowHeight or -tableView:estimatedHeightForRowAtIndexPath: for rows and by corresponding methods for section headers and footers. By setting estimatedRowHeight to 60, you telling UITableView to skip three rows (60 * 3 = 180) and to place row 4 with offset of -40 from top visible edge. Hence visual "jump" by 40 pixels up.
A "right" solution here would be not to call reloadData. Reload rows only for changed objects instead, use -reloadRowsAtIndexPaths:withRowAnimation:. In case of NSFetchedResultsController + UITableView use this classic scheme.

UITableViewCell size does not follow Content View size

I have two UITableViewControllers which use the same set of cell templates, therefore I have created a table view controller in my Storyboard which contains these templates. My table view controllers instantiate an instance of this template holder tableview, then in -tableView:cellForRowAtIndexPath: they deque one of it's cells, and configure it as they need.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *widgetDict = [self.widgets objectAtIndex:indexPath.row];
DMSWidget *widget = [self.widgetStorageTableViewController.tableView dequeueReusableCellWithIdentifier:#"Key Value Row"];
...
return widget;
}
These cells have their inner layouts set up entirely using AutoLayout.
Everything works fine, except that I get cells which have the standard 44 px height and width of the table view, with Content Views narrower than this (depending which they contain, the ones with less contents are smaller), and are longer than the cells themselves. So the Content Views overlap the next cell but don't fill the entire width.
I don't understand, how this could happen. How is it possible, that the UITableViewCell has different size than it's Content View? They should be the same, isn't it?
What I tried to do but failed:
implement -tableView:heightForRowatIndexPath:: tried to return UITableViewAutomaticDimension, but did not help
reload my table view when the sizes are calculated: does nothing
add hacks to cell implementations, like setting autoresizingMask in -awakeFromNib: I could make the Content View expand to the width of the cell itself, but it was still overlapping to the next cell
UPDATE: It looks like it is not related to the dual table view architecture. When I copy my cell to the tableView where it will show up, has the same issues.
I've found the solution: it was all my fault. In some of the ancestors of my cell has an init, which called -initWithFrame: explicitly with CGRectZero. After removing this, iOS 8 could use it's frame values and everything worked fine.

Resources