TL;DR: Don’t love studying? Soar straight to the pattern tasks on GitHub:
The primary 2 steps under are relevant no matter which iOS variations you’re creating for.
1. Set Up & Add Constraints
In your UITableViewCell
subclass, add constraints in order that the subviews of the cell have their edges pinned to the sides of the cell’s contentView (most significantly to the highest AND backside edges). NOTE: do not pin subviews to the cell itself; solely to the cell’s contentView
! Let the intrinsic content material dimension of those subviews drive the peak of the desk view cell’s content material view by ensuring the content material compression resistance and content material hugging constraints within the vertical dimension for every subview will not be being overridden by higher-priority constraints you’ve gotten added. (Huh? Click on right here.)
Keep in mind, the thought is to have the cell’s subviews related vertically to the cell’s content material view in order that they’ll “exert strain” and make the content material view develop to suit them. Utilizing an instance cell with a number of subviews, here’s a visible illustration of what some (not all!) of your constraints would wish to appear to be:
You’ll be able to think about that as extra textual content is added to the multi-line physique label within the instance cell above, it might want to develop vertically to suit the textual content, which can successfully drive the cell to develop in top. (After all, you could get the constraints proper to ensure that this to work accurately!)
Getting your constraints proper is unquestionably the hardest and most necessary half of getting dynamic cell heights working with Auto Structure. When you make a mistake right here, it may stop every little thing else from working — so take your time! I like to recommend organising your constraints in code as a result of you already know precisely which constraints are being added the place, and it is lots simpler to debug when issues go mistaken. Including constraints in code might be simply as simple as and considerably extra highly effective than Interface Builder utilizing format anchors, or one of many implausible open supply APIs accessible on GitHub.
- When you’re including constraints in code, you need to do that as soon as from inside the
updateConstraints
technique of your UITableViewCell subclass. Word thatupdateConstraints
could also be known as greater than as soon as, so to keep away from including the identical constraints greater than as soon as, be certain to wrap your constraint-adding code insideupdateConstraints
in a verify for a boolean property similar todidSetupConstraints
(which you set to YES after you run your constraint-adding code as soon as). However, you probably have code that updates present constraints (similar to adjusting thefixed
property on some constraints), place this inupdateConstraints
however exterior of the verify fordidSetupConstraints
so it may run each time the tactic is known as.
2. Decide Distinctive Desk View Cell Reuse Identifiers
For each distinctive set of constraints within the cell, use a singular cell reuse identifier. In different phrases, in case your cells have a couple of distinctive format, every distinctive format ought to obtain its personal reuse identifier. (An excellent trace that you could use a brand new reuse identifier is when your cell variant has a special variety of subviews, or the subviews are organized in a definite trend.)
For instance, in case you have been displaying an e mail message in every cell, you might need 4 distinctive layouts: messages with only a topic, messages with a topic and a physique, messages with a topic and a photograph attachment, and messages with a topic, physique, and picture attachment. Every format has utterly completely different constraints required to attain it, so as soon as the cell is initialized and the constraints are added for one in all these cell sorts, the cell ought to get a singular reuse identifier particular to that cell kind. This implies while you dequeue a cell for reuse, the constraints have already been added and are able to go for that cell kind.
Word that on account of variations in intrinsic content material dimension, cells with the identical constraints (kind) should still have various heights! Do not confuse basically completely different layouts (completely different constraints) with completely different calculated view frames (solved from similar constraints) on account of completely different sizes of content material.
- Don’t add cells with utterly completely different units of constraints to the identical reuse pool (i.e. use the identical reuse identifier) after which try and take away the outdated constraints and arrange new constraints from scratch after every dequeue. The inner Auto Structure engine is just not designed to deal with massive scale modifications in constraints, and you will notice huge efficiency points.
For iOS 8 – Self-Sizing Cells
3. Allow Row Peak Estimation
To allow self-sizing desk view cells, you could set the desk view’s
rowHeight property to UITableViewAutomaticDimension. You will need to additionally
assign a worth to the estimatedRowHeight property. As quickly as each of
these properties are set, the system makes use of Auto Structure to calculate the
row’s precise top
With iOS 8, Apple has internalized a lot of the work that beforehand needed to be carried out by you previous to iOS 8. As a way to permit the self-sizing cell mechanism to work, you could first set the rowHeight
property on the desk view to the fixed UITableView.automaticDimension
. Then, you merely must allow row top estimation by setting the desk view’s estimatedRowHeight
property to a nonzero worth, for instance:
self.tableView.rowHeight = UITableView.automaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to no matter your "common" cell top is
What this does is present the desk view with a brief estimate/placeholder for the row heights of cells that aren’t but onscreen. Then, when these cells are about to scroll on display, the precise row top will likely be calculated. To find out the precise top for every row, the desk view mechanically asks every cell what top its contentView
must be based mostly on the recognized fastened width of the content material view (which relies on the desk view’s width, minus any extra issues like a piece index or accent view) and the auto format constraints you’ve gotten added to the cell’s content material view and subviews. As soon as this particular cell top has been decided, the outdated estimated top for the row is up to date with the brand new precise top (and any changes to the desk view’s contentSize/contentOffset are made as wanted for you).
Typically talking, the estimate you present would not need to be very correct — it’s only used to accurately dimension the scroll indicator within the desk view, and the desk view does a superb job of adjusting the scroll indicator for incorrect estimates as you scroll cells onscreen. You need to set the estimatedRowHeight
property on the desk view (in viewDidLoad
or comparable) to a relentless worth that’s the “common” row top. Provided that your row heights have excessive variability (e.g. differ by an order of magnitude) and also you discover the scroll indicator “leaping” as you scroll do you have to trouble implementing tableView:estimatedHeightForRowAtIndexPath:
to do the minimal calculation required to return a extra correct estimate for every row.
For iOS 7 assist (implementing auto cell sizing your self)
3. Do a Structure Cross & Get The Cell Peak
First, instantiate an offscreen occasion of a desk view cell, one occasion for every reuse identifier, that’s used strictly for top calculations. (Offscreen which means the cell reference is saved in a property/ivar on the view controller and by no means returned from tableView:cellForRowAtIndexPath:
for the desk view to truly render onscreen.) Subsequent, the cell have to be configured with the precise content material (e.g. textual content, pictures, and so on) that it might maintain if it have been to be displayed within the desk view.
Then, drive the cell to right away format its subviews, after which use the systemLayoutSizeFittingSize:
technique on the UITableViewCell
‘s contentView
to seek out out what the required top of the cell is. Use UILayoutFittingCompressedSize
to get the smallest dimension required to suit all of the contents of the cell. The peak can then be returned from the tableView:heightForRowAtIndexPath:
delegate technique.
4. Use Estimated Row Heights
In case your desk view has greater than a pair dozen rows in it, you can find that doing the Auto Structure constraint fixing can rapidly bathroom down the primary thread when first loading the desk view, as tableView:heightForRowAtIndexPath:
is known as on each row upon first load (so as to calculate the scale of the scroll indicator).
As of iOS 7, you’ll be able to (and completely ought to) use the estimatedRowHeight
property on the desk view. What this does is present the desk view with a brief estimate/placeholder for the row heights of cells that aren’t but onscreen. Then, when these cells are about to scroll on display, the precise row top will likely be calculated (by calling tableView:heightForRowAtIndexPath:
), and the estimated top up to date with the precise one.
Typically talking, the estimate you present would not need to be very correct — it’s only used to accurately dimension the scroll indicator within the desk view, and the desk view does a superb job of adjusting the scroll indicator for incorrect estimates as you scroll cells onscreen. You need to set the estimatedRowHeight
property on the desk view (in viewDidLoad
or comparable) to a relentless worth that’s the “common” row top. Provided that your row heights have excessive variability (e.g. differ by an order of magnitude) and also you discover the scroll indicator “leaping” as you scroll do you have to trouble implementing tableView:estimatedHeightForRowAtIndexPath:
to do the minimal calculation required to return a extra correct estimate for every row.
5. (If Wanted) Add Row Peak Caching
When you’ve finished all of the above and are nonetheless discovering that efficiency is unacceptably gradual when doing the constraint fixing in tableView:heightForRowAtIndexPath:
, you will sadly must implement some caching for cell heights. (That is the method prompt by Apple’s engineers.) The overall concept is to let the Autolayout engine remedy the constraints the primary time, then cache the calculated top for that cell and use the cached worth for all future requests for that cell’s top. The trick in fact is to be sure you clear the cached top for a cell when something occurs that might trigger the cell’s top to alter — primarily, this may be when that cell’s content material modifications or when different necessary occasions happen (just like the consumer adjusting the Dynamic Kind textual content dimension slider).
iOS 7 Generic Pattern Code (with a lot of juicy feedback)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Decide which reuse identifier ought to be used for the cell at this
// index path, relying on the actual format required (you will have
// only one, or might have many).
NSString *reuseIdentifier = ...;
// Dequeue a cell for the reuse identifier.
// Word that this technique will init and return a brand new cell if there is not
// one accessible within the reuse pool, so both method after this line of
// code you'll have a cell with the right constraints able to go.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// Configure the cell with content material for the given indexPath, for instance:
// cell.textLabel.textual content = someTextForThisCell;
// ...
// Be sure the constraints have been arrange for this cell, because it
// might have simply been created from scratch. Use the next strains,
// assuming you're organising constraints from inside the cell's
// updateConstraints technique:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// In case you are utilizing multi-line UILabels, do not forget that the
// preferredMaxLayoutWidth must be set accurately. Do it at this
// level in case you are NOT doing it inside the UITableViewCell subclass
// -[layoutSubviews] technique. For instance:
// cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Decide which reuse identifier ought to be used for the cell at this
// index path.
NSString *reuseIdentifier = ...;
// Use a dictionary of offscreen cells to get a cell for the reuse
// identifier, making a cell and storing it within the dictionary if one
// hasn't already been added for the reuse identifier. WARNING: Do not
// name the desk view's dequeueReusableCellWithIdentifier: technique right here
// as a result of this can end in a reminiscence leak because the cell is created however
// by no means returned from the tableView:cellForRowAtIndexPath: technique!
UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
if (!cell) {
cell = [[YourTableViewCellClass alloc] init];
[self.offscreenCells setObject:cell forKey:reuseIdentifier];
}
// Configure the cell with content material for the given indexPath, for instance:
// cell.textLabel.textual content = someTextForThisCell;
// ...
// Be sure the constraints have been arrange for this cell, because it
// might have simply been created from scratch. Use the next strains,
// assuming you're organising constraints from inside the cell's
// updateConstraints technique:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// Set the width of the cell to match the width of the desk view. This
// is necessary in order that we'll get the right cell top for various
// desk view widths if the cell's top is determined by its width (on account of
// multi-line UILabels phrase wrapping, and so on). We do not want to do that
// above in -[tableView:cellForRowAtIndexPath] as a result of it occurs
// mechanically when the cell is used within the desk view. Additionally notice,
// the ultimate width of the cell is probably not the width of the desk view in
// some circumstances, for instance when a piece index is displayed alongside
// the correct facet of the desk view. You will need to account for the decreased
// cell width.
cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
// Do the format move on the cell, which can calculate the frames for
// all of the views based mostly on the constraints. (Word that you could set the
// preferredMaxLayoutWidth on multiline UILabels contained in the
// -[layoutSubviews] technique of the UITableViewCell subclass, or do it
// manually at this level earlier than the under 2 strains!)
[cell setNeedsLayout];
[cell layoutIfNeeded];
// Get the precise top required for the cell's contentView
CGFloat top = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].top;
// Add an additional level to the peak to account for the cell separator,
// which is added between the underside of the cell's contentView and the
// backside of the desk view cell.
top += 1.0;
return top;
}
// NOTE: Set the desk view's estimatedRowHeight property as a substitute of
// implementing the under technique, UNLESS you've gotten excessive variability in
// your row heights and also you discover the scroll indicator "leaping"
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do the minimal calculations required to have the ability to return an
// estimated row top that is inside an order of magnitude of the
// precise top. For instance:
if ([self isTallCellAtIndexPath:indexPath]) {
return 350.0;
} else {
return 40.0;
}
}
These tasks are totally working examples of desk views with variable row heights on account of desk view cells containing dynamic content material in UILabels.
Xamarin (C#/.NET)
When you’re utilizing Xamarin, try this pattern venture put collectively by @KentBoogaart.