I\'ve spent several days trying to figure this out, but there doesn\'t seem to be a solution. I have a very basic UITableView cell with two labels in it. One of them will be
As of iOS 9.1 UIStackView
doesn't implement intrinsicContentSize:
so the table view can't calculate the height of each cell, thus they are displayed at the estimated height instead.
So, ideally you would simplify your code to mean you don't use a stack view and you don't keep adding and removing (and creating and destroying) views all the time. The root of the solution is to not use a stack view though.
You can continue to use a stack view if you want, but you'll need to create a subclass and implement intrinsicContentSize:
. Your reason for using the stack view shouldn't be required though as you can configure the constraints to match first baselines.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *firstLable = firstLable.text;
NSString *secondLable = SecondLable.text;
CGSize constraint = CGSizeMake(cell.frame.size.width/2, 20000.0f);
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
CGRect firstLableRect = [firstLable boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont fontWithName:@"Your App Font" size:16.0f],
NSParagraphStyleAttributeName: paragraphStyle.copy} context:nil];
CGRect secondLableRect = [secondLable boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont fontWithName:@"Your App Font" size:16.0f], NSParagraphStyleAttributeName: paragraphStyle.copy}context:nil];
float max = MAX(firstLableRect.size.height, secondLableRect.size.height) + 20;
//20 for spacing 10 pc above and 10 pc below
return max ;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=@"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.selectionStyle =UITableViewCellSelectionStyleNone;
NSString *lable1 = @"first label text";
NSString *lable2 = @"second lable text";
CGSize constraint = CGSizeMake(cell.frame.size.width/2-10, 20000.0f);
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
CGRect firstLableRect = [lable1 boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont fontWithName:@"Your App Font" size:16.0f],
NSParagraphStyleAttributeName: paragraphStyle.copy} context:nil];
CGRect secondLableRect = [lable2 boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont fontWithName:@"Your App Font" size:16.0f], NSParagraphStyleAttributeName: paragraphStyle.copy}context:nil];
UILabel *firstLable = [[UILabel alloc]initWithFrame:CGRectMake(5,10,constraint.width,firstLableRect.size.height)];
[firstLable setLineBreakMode:NSLineBreakByWordWrapping];
firstLable.minimumScaleFactor = 15.0f;
[firstLable setNumberOfLines:0];
firstLable.textAlignment = NSTextAlignmentLeft;
[firstLable setFont:[UIFont fontWithName:@"your App font" size:16.0f]];
[cell.contentView addSubview:firstLable];
UILabel *secondLable = [[UILabel alloc]initWithFrame:CGRectMake(cell.frame.size.width/ 2+5,10,constraint.width,secondLableRect.size.height)];
[secondLable setLineBreakMode:NSLineBreakByWordWrapping];
secondLable.minimumScaleFactor = 15.0f;
[secondLable setNumberOfLines:0];
secondLable.textAlignment = NSTextAlignmentLeft;
[secondLable setFont:[UIFont fontWithName:@"your App font" size:16.0f]];
[cell.contentView addSubview:secondLable];
return cell;
}
The height of a table view cell is decided by the table based on the rowHeight
property, or on the return value of optional func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
from the table view delegate.
The height that may be set by the programmer for the UITableViewCell through its frame property is ignored!
Hence you need to figure out the desired height of each cell programatically. Then you need to implement optional func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
from the table view delegate for each of the cells.
If the size of a cell changes while the table view is displayed, you need to either:
reloadData()
from UITableView
, orfunc reloadRowsAtIndexPaths(_ indexPaths: [NSIndexPath], withRowAnimation animation: UITableViewRowAnimation)
from UITableView
.Edit
Actually since you are using constraints in you view it should work without using ``.
Try to replace the code in if categories[parent] == "words" { .. }
with:
cell = tableView.dequeueReusableCellWithIdentifier(childWordsCellIdentifier, forIndexPath: indexPath) as UITableViewCell
// Reset content
for subview in cell.contentView.subviews {
subview.removeFromSuperview()
}
cell.prepareForReuse()
let words = self.page.valueForKey("words")!.allObjects as! [Word]
let wordsInOrder = words.sort({Int($0.order!) < Int($1.order!) })
let word = wordsInOrder[indexPath.row - 1]
let wordLabel = UILabel(frame: CGRectZero)
wordLabel.text = word.valueForKey("sanskrit") as? String
wordLabel.font = UIFont(name: "EuphemiaUCAS-Bold", size: 16)
wordLabel.numberOfLines = 0
wordLabel.translatesAutoresizingMaskIntoConstraints = false
wordLabel.layer.borderColor = UIColor.greenColor().CGColor
wordLabel.layer.borderWidth = 1.0
let englishWordLabel = UILabel(frame: CGRectZero)
englishWordLabel.text = "Foobar don't want to play my game with my anymore."
englishWordLabel.font = UIFont(name: "STHeitiTC-Light", size: 16)
englishWordLabel.numberOfLines = 0
let stackView = UIStackView()
stackView.axis = .Horizontal
stackView.distribution = .FillProportionally
stackView.alignment = .Fill // EDIT
stackView.spacing = 15
stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
stackView.layoutMarginsRelativeArrangement = true
stackView.addArrangedSubview(wordLabel)
stackView.addArrangedSubview(englishWordLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(stackView)
cell.contentView.leadingAnchor.constraintEqualToAnchor(stackView.leadingAnchor).active = true
cell.contentView.topAnchor.constraintEqualToAnchor(stackView.topAnchor).active = true
cell.contentView.trailingAnchor.constraintEqualToAnchor(stackView.trailingAnchor).active = true
cell.contentView.bottomAnchor.constraintEqualToAnchor(stackView.bottomAnchor).active = true
cell.contentView.layoutIfNeeded()
Also I would have used two different cells classes for this task rather than removing the views in the contentView.
Please let me know how it goes!
First of all, unless you need a UIStackView
for some other purpose that you're not mentioning in your question, don't use them. Multi-line labels and dynamic height cells work well without them (don't needlessly complicate the code if they are not necessary).
There are no bugs in iOS that stop this from working, that I'm aware of. Dynamically sized cells work great with Auto Layout, all you need to do is set the correct constraints in the cells and set the rowHeight
and estimatedRowHeight
properties of the table view.
The most likely reason this is not working for you is that you have not put all the needed constraints in your cell. The needed constraints are:
I think you're missing point 4.
Edit
According to your comments, you're using a UIStackView
so as to align the top baselines between the single and the multi-line labels. By this, I understand you want your labels to align like this:
To get this behaviour, you don't need a UIStackView
. Just give the labels constraints to allow the multi-line label to define the cell height. The following image shows the top baselines aligned between the two labels (i.e. the first lines of each label are aligned).
Notice how the left label only needs a leading and top constraint, while the right label needs a top, trailing and bottom, because it defines cell height.
In summary, dynamic table view cells require three things:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
This is very easy.lets go step by step.
1. set the estimatedRowHeight of table view.It makes easy and fast to load the cell.
2.set the rowHeight property of tableview as UITableViewAutomaticDimension.and don't use heightForRowAtIndexPath.
3.put the all top bottom leading and trailing constraint on both Label.its important.
Lets rock and roll no need to use stackView