понедельник, 28 апреля 2014 г.

UITableView with dynamic cell heights. The root.

The most common problem in building tables in iOS is table with dynamic height cells.
First of all, that’s the ideal scheme looks as follows: table view asks delegate for cell height, and then asks data source for configured (possibly new-created) cell.
According to MVC patter, model contains data to present (so it should not calculate cell’s height), controller should «transfer» model properties to view for representing (so it should not calculate cell height, but can ask cell for it’s actual size) and the cell/view representing some data (without understanding and suggestions about what this data means).
So we should set cell content and ask for actual size and than get size. But we should do it before we have any cell. Looks a little  weird in my opinion.
Let's deal with problems as they arise.
I’d prefer create prototype cell which configured exactly the same way as table view cells. So we should do following things: our datasource/delegate should implement method for initial cell configuring (for new created cell) and data cell configuring (for data representing). When we need to calculate cell height we just use common method to configure cell and configure prototype cell. Then we just call sizeThatFit using size with table view width and zero height. And then we grab prototype cell bounds height.
What’s next? Out prototype cell can hold height for cells to avoid permanent recalculations.
The best part of this method is you can also keep not only height but complex cell components layout! You can create structs with CGRect fields and cell width. You may create NSValue category to keep this structs in any NSDictionary instance (normally using NSIndexPath indexes as keys). So you create structure with fields ‘cell’Width’, ’titleRect’, ‘detailsRet’. And then in first layout keeping calculated values in this structure. Then you keep it in prototype cell. Next time you just check if prototype cell has recalculated layout for given cell width. Pretty simple.
Surely, we have bad sides. First of all, layout caching implementation normally is a good piece of code. For every cell class. But hey, complex cell normally has big implementation.
Also, we have to inherit cells from some «able-to-prototype» cell class. Surely, you can use protocols but it’s just give you more problems. That is the task I working on.

Yep, that issue is common problem and every developer looks for it's own decision. 
So the overall answer is just does not exist. You can also calculate cell heights in view controller (not very well but useful, actually), you can use UICollectionView (in iOS 7 still have nasty bugs) or you can avoid of using UITableView and use power of it's parent - UIScrollView (Yep, in some cases you can just add subviews to UIScrollView. That's reasonable when you have less-repeating cache-meanless data to represent).

More code, less words in future.