iOS - Dynamic height, data driven, vertically scrollable layout - ios

I am trying to create the layout in the image
Here is the main requirements:
The content in each section is data driven and retrieved from the api.
The height of each section is dynamic, based on how many fields we have.
The scene height is dynamic.
The scene is vertically scrollable.
Here is what I tried to do:
- Using static cell UITableView
- Using stack view with a scroll view
In both cases I got stuck with dealing with stack views for the dynamic data fields, and I struggled in handling the dynamic height for each section.
Any Ideas?

Your requirement is pretty straight forward and can be met using a UITableView. Use dynamic UITableViewCell. The reason i choose dynamic table view cell is that you can resize the cell height according to content. Measure your content size inside the tableView:heightForRowAtIndexPath:.
The flow will be following:
Initiate the api call
While the data isn't available return the cell height according to your placeholder view (i.e placeholder image height)
While the data is available, process your data and reload the table view
After reloading the table view, table view will ask each cell's height once again, so this is the chance you get to calculate the content height. (I assume that you can measure the string height while the string width and font is given. Plz google that, thats obvious.)
return that calculated height.
Optimization Possibilities:
Instead of recalculating the cell height each time, you can calculate the cell height once and store that height inside a simple dictionary keyed with IndexPath.

Related

Is it possible to use self sizing cells within self sizing cells?

I want to have self sizing cells within a tableview with self sizing cells. But on first initialisation some cells are not correct. After a scroll to the bottom and the cells will display again the cell size is correct. In the screenshot you can see that the top 2 cells have a white space at the bottom and the third one doesn't. They should all look like the third one.
This is the issue in my main project.
Screenshot
The problem with automatically sizing cells inside automatically sized cells is a bit tricky because you have to understand how it works. UITableView works with estimates most of the time. It usually does not calculate the contentSize precisely because to calculate it precisely, it has to first instantiate every cell, layout it and then calculate its size.
The precise values are calculated only for cells that are displayed (visible in current scroll frame).
The tricky part is that the inner cells (inside your outer cell) are not displayed until the outer cell is displayed therefore the outer cell does not have size calculated correctly. Also note that UITableView does not automatically update cell heights unless explicitly said to do so.
The solution, if you really have to do this, is to calculate the height of the outer cell correctly before it is displayed and manually set a height constraint.
If you know the height (from data source), it's easy. If you actually need to calculate the height of the inner table, you can do something like this:
// make the table high enough to display all cells
innerTableHeightConstraint.constant = 2000
// reload table
innerTable.reloadData()
// force layout
innerTable.layoutIfNeeded()
// now the contentSize is correctly calculated
innerTableHeightConstraint.constant = innerTable.contentSize.height
The whole concept is tricky and ideally you should prefer using UICollectionView or table sections. When you are using inner table views, there won't be any cell reuse for the inner tables and your performance will suffer.
You should not wrap UITableView into UITableViewCell, try to use UITableView Sections instead to add an extra depth level to your UITableView.

Attach/Stick a UIButton under the UITableView

This illustration shows what i'm trying to do:
The green list is the UITableView where it dynamically adjust it's height based on the number of items inside of it.
Underneath of the UITableView is a button that should follow the UITableView whenever it changes it's height size.
The UIButton should always be beneath the UITableView whatever the size of the UItableView.
I'm currently using autoresizing for UITableView
I have tried to use Autolayout but it seems i can't still find the answer.
i currently have no constraints in the layout.
This boils down to calculating the height of the table view that perfectly fits the cells. Basically you need to measure the size of every cell, then create a height constraint on the table view, and set its constant to the sum of the cells' heights.
Measuring the height of cells is tricky thought. If you only have a few cells (like in your illustrations), you can just instantiate all of them, keep them in an array and use systemLayoutSizeFittingSize to calculate their sizes. If you use multi-line labels, it is also important to set their preferredMaxLayoutWidth to appropriate values.
However, if you have only a few cells (and so cell reuse is not important), stack view is probably a better choice than table view. It's just too tricky to calculate the perfect height of a table view.

Can hard coded and self-sizing table cells be intermixed within a tableview?

I'm working on an app that will populate the tableview based on a web service response. So far the two content types I know I will be getting will be data shown in a textview, and also in a collection view. I've begun implementing Ash Furrow's AFCollectionView. The dynamic sizing for text areas is working, however the collection view is scrolling within a cell height that is even less than my estimatedRowHeight (210.0).
Because the collection view table cell is not correctly resizing is it possible to explicitly set the height for the collectionView cell(s), and let any of the text based cells remain dynamic?
Your self sizing cells use auto-layout to determine what height to be, and that's based on the intrinsic content size of the subviews. But, that's not the only way auto-layout can specify the height of a view. You can add a static height constraint to your cells which contain a collection view.

Have UITableViewCell resize itself with autolayout

I have 3 labels in a UITableViewCell and have the labels set so they will wordwrap. If they word wrap the text goes into the next cell. How do I get AutoLayout to expand the cell based on the content without having to write code in the heightForRowAtIndex method? Isn't there a constraint I can use to automatically adjust the cell based on the contentView?
The cell looks fine if the text doesn't wrap in the label. Once it wraps that is when the problem occurs and I would like to have the cell resize to fit the content and have the same spacing between the bottom label and the bottom as there is between the top and top label.
Unfortunately no, you can't do this. A table view calculates its own total height first and has a fixed idea of the size of each cell as they load, it won't determine it's height from the outside in and it won't let layout constraints change the height of a cell.
If you think about how tables work, with cell reuse, then you couldn't really size the table from its cells without loading in every cell and adding it to the scrollview, and performing a layout pass on the whole thing. That would probably lead to quite poor performance.
You could experiment with populating a "free" cell (i.e a cell you've just instantiated, not added to a table) and laying it out for each row in your datasource when calculating heightForRow.
As you are loading the individual cells, after you fill the labels, but before you load the instance of the cell, check the label height.
Something like:
cell.frame.size.height
If the height is large enough that you know the label has wrapped to two lines, then increase the height of the cell you are about to load.

UITableView scrollable content size

I am trying to achieve a newsstand like effect with a scrolling and repeating background. I could do this using a UITableView if I could set it's content size (An inherited method that seems to be overridden by something else in the UITableView Code) in order to fill the view with unscrollable cells.
Currently I am planning to make a custom uiscrollview. Which will be more complex and won't have the cell reuse.
So, is there a way to set a UITableView's content size.
You could semi-hack a 'contentSize' kind of control over a UITableView by simply setting the number of cells that you have (let's assume you have a 1 section table view and aren't using a 'grouped' styled table) to:
your desired height contentSize height / height for each cell (default is 44)
Now, your problem is that if you only have, say, three rows' worth of data to display, what to do with the rest of the cells? Well, simply set their backgroundColor to the same as the table's background color and their selectionStyle property to UITableViewCellSelectionStyleNone. Wham, now you have invisible cells, and can set the 'contentSize' of the table.
Note that your contentSize will only have a resolution of the height of your cells, if you leave the cell heights at their default 44, then you can only set the contentSize to a multiple of 44.

Resources