UITableViewCell's contentView gets unwanted "height==44" constraint - ios

I'm creating my UI entirely in code and using Masonry to constrain the cell's content view's subviews to the appropriate height. I'm using
[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height
on iOS 7 for the row height, while iOS 8 handles it automatically.
Everything looks exactly as it should on screen, but in the console I get trainloads of warnings for conflicting constraints, which all seem to be caused by an unasked and unnecessary height constraint on the cell's content view (e.g. <NSLayoutConstraint UITableViewCellContentView.height == 44>).
On iOS 8 I'm setting the table view's rowHeight as UITableViewAutomaticDimension (effectively -1) but still I get this constraint. I'm only adding constraints between the content view and its own subviews, so no constraints between the content view and the cell itself.
Any idea where this constraint comes from and how to make it go away?
Edit: Now I actually found a "solution" of sorts – initially setting the content view's frame to something ridiculous, like CGRectMake(0, 0, 99999, 99999), before adding subviews or constraints, seems to make the warnings go away. But this doesn't exactly smell like the right way to do it, so can anyone tell of a better approach?

I had the same issue and fixed it setting the auto resizing mask of the cell like this:
override func awakeFromNib() {
super.awakeFromNib()
self.contentView.autoresizingMask = .flexibleHeight
}
Also in the controller I set the estimated height and tell the table view to use automatic dimension (in the viewDidLoad method:
self.tableView.estimatedRowHeight = 120
self.tableView.rowHeight = UITableView.automaticDimension
These links helped:
http://useyourloaf.com/blog/2014/08/07/self-sizing-table-view-cells.html
Auto layout constraints issue on iOS7 in UITableViewCell
Hope this helps!

To tack on to the accept answer- after months of trying to get iOS 8's automatic cell sizing to work I discovered an important caveat. The 'estimatedRowHeight' property MUST be set. Either via the tableView directly or by implementing the delegate methods. Even if there's no way to determine a valid estimate, simply providing a value other than the default (0.0) has demonstrably allowed iOS 8's cell layout to work in my testing.

Regarding to the "solution" mentioned in the edit in the question (setting the contentView frame to something big temporarily), here's proof this is a good "solution":
https://github.com/smileyborg/TableViewCellWithAutoLayoutiOS8/blob/master/TableViewCellWithAutoLayout/TableViewController/TableViewCell.swift
// Note: if the constraints you add below require a larger cell size than the current size (which is likely to be the default size {320, 44}), you'll get an exception.
// As a fix, you can temporarily increase the size of the cell's contentView so that this does not occur using code similar to the line below.
// See here for further discussion: https://github.com/Alex311/TableCellWithAutoLayout/commit/bde387b27e33605eeac3465475d2f2ff9775f163#commitcomment-4633188
// contentView.bounds = CGRect(x: 0.0, y: 0.0, width: 99999.0, height: 99999.0)
It's hacky but it seems to work.

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
//self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self.itemView = [CustomItemView new];
[self.contentView addSubview:self.itemView];
}
return self;
}
set translatesAutoresizingMaskIntoConstraints to NO is not work for me
, but autoresizingMask = UIViewAutoresizingFlexibleHeight is well.
you should also making constraints like this:
- (void)updateConstraints {
[self.itemView mas_updateConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.top.equalTo(0);
//make.bottom.equalTo(0);
make.bottom.lessThanOrEqualTo(0);
}];
[super updateConstraints];
}
bottom constraints not just equalTo contentView's bottom, you should use lessThanOrEqualTo
hope this is work to you!

I found out that in some cases, setting an estimatedHeight that is many times bigger the height of my average cell fixed most if not all warnings and had no negative impact on the display.
i.e.:
Setting self.tableView.estimatedRowHeight = 500.0f while most rows are only about 100.0f in height fixed my issue.

Related

translatesAutoresizingMaskIntoConstraints and UILabel

I have created this simple custom UIView
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
internalView = [UIView new];
[self addSubview:internalView];
internalView.backgroundColor = [UIColor whiteColor];
sublabel = [UILabel new];
sublabel.text = #"test";
sublabel.backgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:0.4];
sublabel.textAlignment = NSTextAlignmentCenter;
sublabel.translatesAutoresizingMaskIntoConstraints = NO;
[internalView addSubview:sublabel];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
[internalView setFrame:self.bounds];
[sublabel setFrame:internalView.bounds];
}
#end
The view is used in a xib where are defined 4 constraints:
The constrains are related to the top, trailing, leading and the height.
The views hierarchy of the custom view is self -> internalView -> sublabel
The layoutSubviews is doing a simple thing like setting the frame of each subview with the bounds of the superview.
I created the code above just to point out a strange behaviour i found in translatesAutoresizingMaskIntoConstraints.
(The yellow view is the label)
If the value of the property is YES the results is what I expect from the code in the layoutSubviews method
If it is NO, with the same layoutSubviews i got:
The documentation of the translatesAutoresizingMaskIntoConstraints says:
If this is value is YES, the view’s superview looks at the view’s
autoresizing mask, produces constraints that implement it, and adds
those constraints to itself (the superview).
In both cases the autoresizingMask is set to UIViewAutoresizingNone and there are no constraints in the constraints property of the label.
Why with the translatesAutoresizingMaskIntoConstraints=NO the frame that i set in the layoutSubviews is not what i see on screen?
Edit 2:
I expect, given the exposed scenario, to have the same results with translatesAutoresizingMaskIntoConstraints set to YES or NO.
Edit:
I tried to swizzle the sizeToFit method to check if it is called. It's not.
This is happen in iOS6/7
Update 08/08/14
After further investigation i discovered that there is a way to change the frame of the label without having autolayout playing around.
I discovered that after the first pass of layout (when is called the layoutSubviews) with the translatesAutoresizingMaskIntoConstraints=NO autolayout adds constraints for the hugging/compression of the UILabel. The point is that for every view that implements intrinsicContentSize returning something different from UIViewNoIntrinsicMetric the autolayout adds specific constrains. That is the reason behind the resizing of the label.
So the first thing that i did is to reimplement a subclass of the UILabel to override the
intrinsicContentSize.
After that, following the suggestions in this really good article http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html, I tried to turn of autolayout completely for the subviews involved removing [super layoutSubviews].
The goal for me was to avoid that autolayout could act on views where a was trying to apply animated transformations. So if you have the same needs i hope this can help you.
This comes more from intuition of having used it than actual study, but...
If you set translatesAutoresizingMaskIntoConstraints to YES, the system will create constraints to enforce the frame you defined for your view.
If it is set to NO, no constraints will be set for the view, and as you did not set them yourself either, the view will be resized according to default behaviours. In this case it seems to resize to the minimum content size because of the "contentHugging" values.
Bottom line is, from my understanding, when auto-layout is active all views need constraints to be properly placed. You either set that property to YES, or set the constraints yourself. If you don't do either, results will be a bit unpredictable.
The UIViewAutoresizingNone mask is still a valid mask, with fixed dimensions (no resizing) and it will be translated to constraints. Views can coexist without setting constraints, when you set that option to YES.
You are interpreting UIViewAutoresizingNone as meaning "no mask" when it really means "mask with no resizing". Sorry to disagree with you, but I think that this is the expected behaviour :)

iOS8 Beta 3 UITableViewCell layoutSubviews infinite loop

There were auto-resizable table view cells introduced in iOS8 (WWDC Session 226 What's new in table and collection views).
In my project I'm trying to implement old fixed rows height behavior. Also, the most important thing for me is to inset default cell.contentView frame with margins on the left and right side.
So, I change the cell.contentView.frame property and immediately after that the -[cell setNeedsLayout] method is being called and and it leads to cell get stuck in an infinite layoutSubviews loop.
Steps to reproduce:
Create new single view project and replace default view controller with the table view controller
Disable table view automatic height calculation
-(void)viewDidLoad {
[super viewDidLoad];
self.tableView.estimatedRowHeight = 0;
self.tableView.rowHeight = 44;
}
3. Drop a custom cell in the table view in storyboard,
Add any subview to the custom cell:,
Subclass the cell and change contentView.frame in layoutSubviews,
Build and run.
Result:
Simulator ends up in a black screen stuck in a infinite layout subviews loop.
Expected Result:
Simulator displaying a table view with a cell's contentView having a custom frame.
Comment:
While debugging a little bit, I found that the infinite loop can be avoided if the cell does not have any custom subviews dropped on it. So it seems the bug will appear after the following conditions are met:
self.tableView.estimatedRowHeight = 0; self.tableView.rowHeight = 44;
cell.contentView.frame = CGRectMake(...)
cell have custom subviews dropped on it in xib or storyboard
Apple does not have the issue in the "Known issues" list for iOS8, so I'm wondering is it actually a bug in iOS8 or does anybody know how to resolve the issue?
This is not a bug: setNeedsLayout will be called any time you change view's frame.
My guess is that changing cell.contentView.frame also changes cell.bounds in iOS 8, triggering relayout. This behavior may be different between iOS versions; anyway, those are standard views, so we shouldn't change them in unsupported ways.
Rather than operating on cell.contentView, how about adding a custom view with insets to it? Or simply creating a height constraint?
In ViewDidLoad please use the below line and there is no need to give the row height
-(void)viewDidLoad {
[super viewDidLoad];
self.tableView.estimatedRowHeight = 44.0f;
self.tableView.rowHeight = UITableViewAutomaticDimension;
}

UILabel in UITableViewCell with auto layout has wrong height

I have a UITableView with cells that have a fixed height of 100 points. The cells are created in a xib file that uses 3 constraints to pin a UILabel to the left, right and top edges of the cell's contentView. The label's vertical hugging priority is set to 1000 because I want the cell's height to be as small as possible.
When the width of the cell in the xib file is set to 320 points, the same as the tableView's width on the iPhone, autolayout works as expected. However, when I set the width of the cell to less than 320 points, I get unexpected results. (I want to use the same cell in tableViews that have different widths, e.g. in a universal app)
For example: when I set the width to 224 points and give the label a text that takes up 2 lines at that width, the label's height will increase to fit the 2 lines, but when the cell is then resized to 320 points to fit in a tableView of that width, the text only takes up 1 line, but the height of the label remains at 2 lines.
I have put a sample project on GitHub to demonstrate the problem: https://github.com/bluecrowbar/CellLayout
Is there a way to make the UILabel always resize to hug its text content?
Adding this in the cell subclass works:
- (void)layoutSubviews
{
[super layoutSubviews];
[self.contentView layoutIfNeeded];
self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width;
}
I found this on http://useyourloaf.com/blog/2014/02/14/table-view-cells-with-varying-row-heights.html.
Update 1: This answer was for iOS 7. I find auto layout in table view cells to be very unreliable since iOS 8, even for very simple layouts. After lots of experimentation, I (mostly) went back to doing manual layout and manual calculation of the cell's height.
Update 2: I've run some tests on iOS 9 and it seems that UITableViewAutomaticDimension finally works as advertised. Yay!
Stupid bug! I've lost almost one day in this problem and finally I solved It with Steven Vandewghe's solution.
Swift version:
override func layoutSubviews() {
super.layoutSubviews()
self.contentView.layoutIfNeeded()
self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width
}
Since you're constraining the label's width, the intrinsicContentSize honors that width and adjusts the height. And this sets up a chicken and egg problem:
The cell's Auto Layout result depends on the label's intrinsicContentSize
The label's intrinsicContentSize depends on the label's width
The label's width depends on the cell's Auto Layout result
So what happens is that the cell's layout is only calculated once in which (2) is based on the static width in the XIB file and this results in the wrong label height.
You can solve this by iterating. That is, repeat the Auto Layout calculation after the label's width has been set by the first calculation. Something like this in your custom cell will work:
- (void)drawRect:(CGRect)rect
{
CGSize size = self.myLabel.bounds.size;
// tell the label to size itself based on the current width
[self.myLabel sizeToFit];
if (!CGSizeEqualToSize(size, self.myLabel.bounds.size)) {
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
}
[super drawRect:rect];
}
original solution does not work reliably:
- (void)layoutSubviews
{
[super layoutSubviews];
// check for need to re-evaluate constraints on next run loop
// cycle after the layout has been finalized
dispatch_async(dispatch_get_main_queue(), ^{
CGSize size = self.myLabel.bounds.size;
// tell the label to size itself based on the current width
[self.myLabel sizeToFit];
if (!CGSizeEqualToSize(size, self.myLabel.bounds.size)) {
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
}
});
}
I'm using XCode 10 with iOS 12 and I still get autolayout problems with cells not being given the correct height when the table is first presented. Timothy Moose's answer didn't fix the problem for me, but based on his explanation I came up with a solution which does work for me.
I subclass UITableViewController and override the viewDidLayoutSubviews message to check for width changes, and then force a table update if the width does change. This fixes the problem before the view is presented, which makes it look much nicer than my other efforts.
First add a property to your custom UITableViewController subclass to track the previous width:
#property (nonatomic) CGFloat previousWidth;
Then override viewDidLayoutSubviews to check for width changes:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat width = self.view.frame.size.width;
if (self.previousWidth != width) {
self.previousWidth = width;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
}
This fixed issues with my table cells sometimes being given the wrong height initially.
I know this is an old issue, but maybe this UILabel subclass can also help for some:
class AutoSizeLabel: UILabel {
override var bounds: CGRect {
didSet {
if bounds.size.width != oldValue.size.width {
self.setNeedsUpdateConstraints()
}
}
}
override func updateConstraints() {
if self.preferredMaxLayoutWidth != self.bounds.size.width {
self.preferredMaxLayoutWidth = self.bounds.size.width
}
super.updateConstraints()
}
}
Note: works also for cases when your UILabel won't size itself correctly when inside of a StackView
I usually add these two lines to viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 96
This will automatically resize the cell

Dynamically resize UITableView in UIViewController created in Storyboard

In a UIViewController on a storyboard, I have a UITableView that is sized specifically to have two rows in one section with no header or footer, i.e. the height is 88.0f. There are some cases when I want to add a third row. So in viewWillAppear:animated: (and other logical places) I set the frame to be 44.0f logical pixels higher:
CGRect f = self.tableView.frame;
self.tableView.frame = CGRectMake(f.origin.x, f.origin.y, f.size.width, f.size.height + 44.0f);
NSLog(#"%#",NSStringFromCGRect(self.tableView.frame));
Nothing controversial; pretty standard resize code, and yet... It doesn't work! The tableView height doesn't change visually. The NSLog statement reports the height I expect (132.0f). Is this because I'm using Storyboards? I'm not sure why this isn't working.
Set an auto layout constraint for the height of the table view in your storyboard. Then connect the constraint to an outlet in your view controller so you can access the constraint in your code. Have the constraint be set to 88. When you want to change the height of the table view, just change the constraint's constant to 132.
You can modify the frame only after the call to layoutSubviews is made, which occurs after viewWillAppear. After layoutSubviews is called on the UIVIew you can change the dimensions.
As Gavin suggests, if you have the autolayout enabled you can add the constrains to the UITableView via storyboard, connect the height constraint and modify its value as follow:
constraint.constant = 132.0f
Otherwise if you have the autolayout disabled you can simply change the frame updating the height, but putting the code in a different method, for example viewDidLoad:.
recently I'm try to do what you've do. And I got same problem, tableview height won't change. Now I got the solution, you need to call layoutSubviews after change the frame. And it work on me.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
tableView.frame = CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.frame.size.height + 44.);
[tableView layoutSubviews];
}
don't place it in viewDidLoad or viewWillAppear: because even layoutSubviews is called, the frame won't change. place it on viewDidAppear:

iOS AutoLayout multi-line UILabel

Following question is sort-of continuation of this one:
iOS: Multi-line UILabel in Auto Layout
The main idea is that every view is supposed to state it's "preferred" (intrinsic) size so that AutoLayout can know how to display it properly.
UILabel is just an example of a situation where a view cannot by itself know what size it needs for display. It depends on what width is provided.
As mwhuss pointed out, setPreferredMaxLayoutWidth did the trick of making the label span across multiple lines. But that is not the main question here. The question is where and when do I get this width value that I send as an argument to setPreferredMaxLayoutWidth.
I managed to make something that looks legit, so correct me if I am wrong in any way and tell me please if you know a better way.
In the UIView's
-(CGSize) intrinsicContentSize
I setPreferredMaxLayoutWidth for my UILabels according to self.frame.width.
UIViewController's
-(void) viewDidLayoutSubviews
is the first callback method I know where subviews of the main view are appointed with their exact frames that they inhabit on the screen. From inside that method I, then, operate on my subviews, invalidating their intrinsic sizes so that UILabels are broken into multiple lines based on the width that was appointed to them.
There's an answer this question on objc.io in the "Intrinsic Content Size of Multi-Line Text" section of Advanced Auto Layout Toolbox. Here's the relevant info:
The intrinsic content size of UILabel and NSTextField is ambiguous for multi-line text. The height of the text depends on the width of the lines, which is yet to be determined when solving the constraints. In order to solve this problem, both classes have a new property called preferredMaxLayoutWidth, which specifies the maximum line width for calculating the intrinsic content size.
Since we usually don’t know this value in advance, we need to take a two-step approach to get this right. First we let Auto Layout do its work, and then we use the resulting frame in the layout pass to update the preferred maximum width and trigger layout again.
The code they give for use inside a view controller:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
[self.view layoutIfNeeded];
}
Take a look at their post, there's more information about why it's necessary to do the layout twice.
It seems annoying that a UILabel doesn't default to its width for the preferred max layout width, if you've got constraints that are unambiguously defining that width for you.
In nearly every single case I've used labels under Autolayout, the preferred max layout width has been the actual width of the label, once the rest of my layout has been performed.
So, to make this happen automatically, I have used a UILabel subclass, which overrides setBounds:. Here, call the super implementation, then, if it isn't the case already, set the preferred max layout width to be the bounds size width.
The emphasis is important - setting preferred max layout causes another layout pass to be performed, so you can end up with an infinite loop.
Update
My original answer appears to be helpful so I have left it untouched below, however, in my own projects I have found a more reliable solution that works around bugs in iOS 7 and iOS 8.
https://github.com/nicksnyder/ios-cell-layout
Original answer
This is a complete solution that works for me on iOS 7 and iOS 8
Objective C
#implementation AutoLabel
- (void)setBounds:(CGRect)bounds {
if (bounds.size.width != self.bounds.size.width) {
[self setNeedsUpdateConstraints];
}
[super setBounds:bounds];
}
- (void)updateConstraints {
if (self.preferredMaxLayoutWidth != self.bounds.size.width) {
self.preferredMaxLayoutWidth = self.bounds.size.width;
}
[super updateConstraints];
}
#end
Swift
import Foundation
class EPKAutoLabel: UILabel {
override var bounds: CGRect {
didSet {
if (bounds.size.width != oldValue.size.width) {
self.setNeedsUpdateConstraints();
}
}
}
override func updateConstraints() {
if(self.preferredMaxLayoutWidth != self.bounds.size.width) {
self.preferredMaxLayoutWidth = self.bounds.size.width
}
super.updateConstraints()
}
}
We had a situation where an auto-layouted UILabel inside a UIScrollView laid out fine in portrait, but when rotated to landscape the height of the UILabel wasn't recalculated.
We found that the answer from #jrturton fixed this, presumably because now the preferredMaxLayoutWidth is correctly set.
Here's the code we used. Just set the Custom class from Interface builder to be CVFixedWidthMultiLineLabel.
CVFixedWidthMultiLineLabel.h
#interface CVFixedWidthMultiLineLabel : UILabel
#end
CVFixedWidthMultiLineLabel.m
#implementation CVFixedWidthMultiLineLabel
// Fix for layout failure for multi-line text from
// http://stackoverflow.com/questions/17491376/ios-autolayout-multi-line-uilabel
- (void) setBounds:(CGRect)bounds {
[super setBounds:bounds];
if (bounds.size.width != self.preferredMaxLayoutWidth) {
self.preferredMaxLayoutWidth = self.bounds.size.width;
}
}
#end
Using boundingRectWithSize
I resolved my struggle with two multi-line labels in a legacy UITableViewCell that was using "\n" as a line-break by measuring the desired width like this:
- (CGFloat)preferredMaxLayoutWidthForLabel:(UILabel *)label
{
CGFloat preferredMaxLayoutWidth = 0.0f;
NSString *text = label.text;
UIFont *font = label.font;
if (font != nil) {
NSMutableParagraphStyle *mutableParagraphStyle = [[NSMutableParagraphStyle alloc] init];
mutableParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attributes = #{NSFontAttributeName: font,
NSParagraphStyleAttributeName: [mutableParagraphStyle copy]};
CGRect boundingRect = [text boundingRectWithSize:CGSizeZero options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
preferredMaxLayoutWidth = ceilf(boundingRect.size.width);
NSLog(#"Preferred max layout width for %# is %0.0f", text, preferredMaxLayoutWidth);
}
return preferredMaxLayoutWidth;
}
Then calling the method was then as simple as:
CGFloat labelPreferredWidth = [self preferredMaxLayoutWidthForLabel:textLabel];
if (labelPreferredWidth > 0.0f) {
textLabel.preferredMaxLayoutWidth = labelPreferredWidth;
}
[textLabel layoutIfNeeded];
As I'm not allowed to add a comment, I'm obliged to add it as an answer.
The version of jrturton only worked for me if I call layoutIfNeeded in updateViewConstraints before getting the preferredMaxLayoutWidth of the label in question.
Without the call to layoutIfNeeded the preferredMaxLayoutWidth was always 0 in updateViewConstraints. And yet, it had always the desired value when checked in setBounds:. I didn't manage to get to know WHEN the correct preferredMaxLayoutWidth was set. I override setPreferredMaxLayoutWidth: on the UILabel subclass, but it never got called.
Summarized, I:
...sublcassed UILabel
...and override setBounds: to, if not already set, set preferredMaxLayoutWidth to CGRectGetWidth(bounds)
...call [super updateViewConstraints] before the following
...call layoutIfNeeded before getting preferredMaxLayoutWidth to be used in label's size calculation
EDIT: This workaround only seems to work, or be needed, sometimes. I just had an issue (iOS 7/8) where the label's height were not correctly calculated, as preferredMaxLayoutWidth returned 0 after the layout process had been executed once. So after some trial and error (and having found this Blog entry) I switched to using UILabel again and just set top, bottom, left and right auto layout constraints. And for whatever reason the label's height was set correctly after updating the text.
As suggested by another answer I tried to override viewDidLayoutSubviews:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
_subtitleLabel.preferredMaxLayoutWidth = self.view.bounds.size.width - 40;
[self.view layoutIfNeeded];
}
This worked, but it was visible on the UI and caused a "visible flicker" i.e. first the label was rendered with the height of two lines, then it was re-rendered with the height of only one line.
This was not acceptable for me.
I found then a better solution by overriding updateViewConstraints:
-(void)updateViewConstraints {
[super updateViewConstraints];
// Multiline-Labels and Autolayout do not work well together:
// In landscape mode the width is still "portrait" when the label determines the count of lines
// Therefore the preferredMaxLayoutWidth must be set
_subtitleLabel.preferredMaxLayoutWidth = self.view.bounds.size.width - 40;
}
This was the better solution for me, because it did not cause the "visual flickering".
A clean solution is to set rowcount = 0 and to use a property for the heightconstraint of your label. Then after the content is set call
CGSize sizeThatFitsLabel = [_subtitleLabel sizeThatFits:CGSizeMake(_subtitleLabel.frame.size.width, MAXFLOAT)];
_subtitleLabelHeightConstraint.constant = ceilf(sizeThatFitsLabel.height);
-(void) updateViewConstraints has a problem since iOS 7.1.
In iOS 8, you can fix multi-line label layout problems in a cell by calling cell.layoutIfNeeded() after dequeuing and configuring the cell. The call is harmless in iOS 9.
See Nick Snyder's answer. This solution was taken from his code at https://github.com/nicksnyder/ios-cell-layout/blob/master/CellLayout/TableViewController.swift.

Resources