The correct API to use is UIView systemLayoutSizeFittingSize:
, passing either UILayoutFittingCompressedSize
or UILayoutFittingExpandedSize
.
For a normal UIView
using autolayout this should just work as long as your constraints are correct. If you want to use it on a UITableViewCell
(to determine row height for example) then you should call it against your cell contentView
and grab the height.
Further considerations exist if you have one or more UILabel's in your view that are multiline. For these it is imperitive that the preferredMaxLayoutWidth
property be set correctly such that the label provides a correct intrinsicContentSize
, which will be used in systemLayoutSizeFittingSize's
calculation.
EDIT: by request, adding example of height calculation for a table view cell
Using autolayout for table-cell height calculation isn't super efficient but it sure is convenient, especially if you have a cell that has a complex layout.
As I said above, if you're using a multiline UILabel
it's imperative to sync the preferredMaxLayoutWidth
to the label width. I use a custom UILabel
subclass to do this:
@implementation TSLabel
- (void) layoutSubviews
{
[super layoutSubviews];
if ( self.numberOfLines == 0 )
{
if ( self.preferredMaxLayoutWidth != self.frame.size.width )
{
self.preferredMaxLayoutWidth = self.frame.size.width;
[self setNeedsUpdateConstraints];
}
}
}
- (CGSize) intrinsicContentSize
{
CGSize s = [super intrinsicContentSize];
if ( self.numberOfLines == 0 )
{
// found out that sometimes intrinsicContentSize is 1pt too short!
s.height += 1;
}
return s;
}
@end
Here's a contrived UITableViewController subclass demonstrating heightForRowAtIndexPath:
#import "TSTableViewController.h"
#import "TSTableViewCell.h"
@implementation TSTableViewController
- (NSString*) cellText
{
return @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
}
#pragma mark - Table view data source
- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView
{
return 1;
}
- (NSInteger) tableView: (UITableView *)tableView numberOfRowsInSection: (NSInteger) section
{
return 1;
}
- (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath
{
static TSTableViewCell *sizingCell;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sizingCell = (TSTableViewCell*)[tableView dequeueReusableCellWithIdentifier: @"TSTableViewCell"];
});
// configure the cell
sizingCell.text = self.cellText;
// force layout
[sizingCell setNeedsLayout];
[sizingCell layoutIfNeeded];
// get the fitting size
CGSize s = [sizingCell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize];
NSLog( @"fittingSize: %@", NSStringFromCGSize( s ));
return s.height;
}
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
TSTableViewCell *cell = (TSTableViewCell*)[tableView dequeueReusableCellWithIdentifier: @"TSTableViewCell" ];
cell.text = self.cellText;
return cell;
}
@end
A simple custom cell:
#import "TSTableViewCell.h"
#import "TSLabel.h"
@implementation TSTableViewCell
{
IBOutlet TSLabel* _label;
}
- (void) setText: (NSString *) text
{
_label.text = text;
}
@end
And, here's a picture of the constraints defined in the Storyboard. Note that there are no height/width constraints on the label - those are inferred from the label's intrinsicContentSize
:
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…