Auto Layout in UITableView for dynamic row heights with table view cell from universal storyboard - uitableview

So far I've had some success in calculating dynamic table cell layouts with auto layout. And so far, I have maintained different storyboards for iPhone and iPad. I would now like to consolidate them into a single storyboard per app. I'm currently stuck at a point where my height calculations do no longer function.
The trouble is apparently that the table view cell as dequeued from the storyboard is still at its fixed storyboard size (relative to a specific device, i.e. iPhone in my case) when the calculation is first required. It will be resized to fit the actual device (iPad in my case) when the table is displayed. But tableView:heightForRowAtIndexPath: is called before that and therefore does its calculation based on the wrong table cell width.
Is there a good way to solve this problem? It seems as if this accepted answer also isn't immune because its instructions also only mention the cell in isolation from its table view.
First, instantiate an offscreen instance of a table view cell, one
instance for each reuse identifier, that is used strictly for height
calculations.

Related

UITableViewCell swapping (or Auto Layout) in an iOS 9 World (2015)

Foreword:
I've read many different approaches to this question, but most are old and rely on checking device orientation and making assumptions about the device being used.
I'm interested in a solution or best practice that works in an iOS 9 world, where "orientation" becomes (mostly) irrelevant and any (universal) app can be presented not only in Compact or Regular size classes, but with wildly varying view sizes within those size classes. See: Split View.
Problem
I have a UITableViewCell. Auto Layout is enabled, and constraints have been added to all subviews within the cell.
Because a UITableView cell can display radically different widths across presentations (e.g. the same cell on an iPhone 5 in portrait vs landscape, or the cell within a modal form sheet presented on an iPad) I need to change the cell's layout/appearance based on the width of the table view, or at least present a different cell that has a different layout/appearance.
i.e. when the cell has a short width (e.g. portrait iPhone), I need to display one set of content. When the cell has a larger width (e.g. landscape iPhone or within a form on an iPad), more elaborate content needs to be displayed.
But...
This does not appear to be a job for Size Classes: the Compact vs Regular division is too broad.
e.g. a tableview presented on an iPhone 5 in portrait will have a Compact horizontal trait. But when the tableview is presented within a form on an iPad (in portrait or landscape), it will also be horizontally "Compact". Yet the two presentations have very different widths for the tableview.
Possible Approach
First, create two versions of the cell in IB: one for short widths, another with a layout for longer widths.
Then, in tableView:cellForRowAtIndexPath: check for the table view's width. Call dequeueReusableCellWithIdentifier with the required cell ID, depending on the table view's width.
This requires doubling up on the work required, but would seem to be plausible.
Problem: tableView:cellForRowAtIndexPath: is not called for visible cells until that cell is scrolled off the screen and back on again.
Possible fix: implement viewWillTransitionToSize:withTransitionCoordinator: and in there call [tableView reloadData]; This will force tableView:cellForRowAtIndexPath: to be called on all visible cells.
Good/bad?
Any suggestions for a sustainable, device-agnostic approach to handle this?

iOS 7+ UITableView with 3 Different Prototype Cells of Varying Height

This should be pretty straight forward, but it appears I need to reach out and ask if others have encountered this...
I've got a project on xcode 6 that's targeting iOS 7+. I'm using a storyboard and autolayout. I've created a UIViewController, dropped on a UITableView, and on that UITableView I've dropped on 3 different prototype cells, each with varying height. Each proto cell has its own UITableViewCell class, with UIElement linked accordingly.
These proto cells are designed in the GUI editor of the storyboard, and each of them have a fixed height. The will never grow in height like this: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
The problem with all examples I keep finding is that they all use a single prototype cell, and vary the height based on the content (i.e. they're using a UITextView and sizing the cell height based on how much text in in the view).
Since my proto cells are already designed in the storyboard, and they will never exceed their design height with dynamic content, all I want to do is show them as they are designed, when they are added to the table.
I'm using a switch statement in the cellForRowAtindexPath to determine which UITableViewCell class to use based on indexPath.row.
I created some dummy data in an array and use it to build a table with 3 rows, to see each of the 3 types of cells being rendered in the table.
This all works perfectly on iOS8. On iOS7, however, all three of my cells visually end up piled on top of each other.
For grins, I then implemented a heightForRowAtIndexPath method, and simply use a switch to determine what indexPath.row I'm dealing with, and then return back the height of the prototype cell (from the dimensions shown in the storyboard designer). This was supposed to be a simple, hard-coded approach to see if heightForRowAtIndexPath was needed to solve the issue.
Again, works great on iOS 8, but on iOS7 all 3 cells are piled on top of each other.
I guess a basic question is: since I'm using storyboard-designed prototype cells, that have fixed sizes and constraints on all of the UIComponent within those proto cells, why doesn't this work on iOS 7?
I'm trying to avoid adding tons of code just make this work on iOS7.
iOS7 doesn't support self-sizing cells, implementing heightForRowAtIndexPath: and returning the correct height is the right way to do it.
Do you calculate height in "tableView:heightForRowAtIndexPath:" ? Maybe you should try to do it in "tableView:estimatedHeightForRowAtIndexPath:", because being dequeue cells copts from storyboard

Can I load a UITableViewCell directly from a xib with a specific size class or trait collection?

I'm trying to do the following in my iOS app:
Embed a UITableView into a parent scroll view; the table view will have its frame expanded to show all its contents (I need to do this because the table view data is a small component of a much larger more complex screen, and I don't want nested scrolling behavior)
I have different layouts for iPhone and iPad, so the cells in this table view are using size classes defined in a table view cell xib.
Since I want the content size of the table view to be accurate, I don't think I can use UITableViewAutomaticDimenstion as the rowheight, so I implement tableView:heightForRowAtIndexPath: and load an instance of my tableview cell from the nib directly and store that view as a property with which I can figure out how tall the cell should be according to autolayout.
My problem stems here; when I load the cell directly from the nib, on iPad the cell uses the constraints and layout in the 'Any' size class as defined in the nib, which is incorrect because the iPad's layout uses the regular width class only in my nib. This causes my table view cell heights to be wrong and too large in my case.
What I need to know is if there's a way to force the trait collection on the cell I load, such that the proper constraints for my views are used on each device type. I can't seem to find anything in the docs that allows for this directly in UIViews, only in UIViewControllers and I'm not keen on holding an offscreen UITableViewCell in a random offscreen UIViewController if i can help it. Any ideas?
After writing this I found this question: Offscreen UITableViewCells (for size calculations) not respecting size class? which seems to ask a similar thing, and the answer in there worked for me (add the offscreen table view cell as a subview of the tableview, or some other view that provides a trait environment). It's not pretty but it seems to work out.

How to set the height of prototype cells in an iOS table views

I have a table view with a prototype cell containing both disclosure and detail accessories. I want to use a font size of 12 in the cell and reduce the row height accordingly. Thus the size of the two accessory icons need to be reduced in size as well. I assumed this would be one of the more obvious things one might want to do with a table view but fail miserably in figuring out how?
Assuming you are have a nib or storyboard, you should see a resize handle on the cells when you select a cell in the editor. Just grab and resize.

Issues regarding dynamic resizing of label and row heights (iOS)

Context:
Building an app that populates a table that takes in data from a asyc json dump.
The cells are of a custom class (I defined). The main label in the cell can be very long.
It is "placed" in storyboard within a prototype cell but customized via code (pretty standard stuff).
Labels are resized in cellForRowAtIndexPath and rows are resized via heightForRowAtIndexPath -- rows are resized by forcing a call to cellForRowAtIndex like Massimo's answer here
So per the question at hand - I've noticed some interesting (bad) things that happen.
First issue: When the table loads, the rows and labels are dynamically resized correctly! Great! However, when I scroll down and then scroll back up, the label heights will be incorrect -- (for example) the first row was correct at loading. Then when I scroll down and then scroll back up to see it again, it will be truncated. Specifically, the row size will be fine but the label height will change and become truncated to 2 lines only. Wondering if this is because I did both storyboard and coding to customize the cell. Anybody see this before?
Second issue: When I scroll down, while the rows are sized correctly (large), the labels are short (truncated.) Wondering if it's some reverse of the above "potential answer".
"potential answer" is that the rows are all calculated and stored "up front" so that scrolling down/then back up doesn't affect it. However, when cells go "out of view" and are dequeued then when they re-viewed (scroll down/then back up) it will rely on the storyboard.(inappropriately?)
All three of your issues are symptomatic of returning the wrong height in heightForRowAtIndexPath. In my data model classes I have a calculateHeight method that I call in heightForRowAtIndexPath. The model also caches the answer so it doesn't have to recalculate it after the first call. The cell class uses the model's calculated height to layout its subviews.
"ANSWERED" by deleting the prototype cell from the storyboard and making them fully in code, the issue went away. The fundamental workings are still not understood (ie. the interactions between storyboard vs. code when cells are put queued and then viewed again)

Resources