I m working on an app where I calculate the height of tableview cell(custom cell) dynamically.The height is calculated perfectly but the label in the cell is truncated. I also tried to set the label's height but still it shows truncated text.
In above screenshot you can see that the long text is not completely shown,
I tried setting the label's height programmatically but it does not work.
Below is the code for setting the height:
let attributes = NSMutableDictionary()
attributes.setValue(MyFonts.HELVETICA_NEUE_REGULAR_15, forKey: NSFontAttributeName)
var cellSize = labelText!.boundingRectWithSize(labelSize!, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: attributes, context: nil)
labelHeight = cellSize.size.height
customCell?.subtitleLabel?.frame.size.height = labelHeight;
Kindly suggest any solution for this.
If your cell is created with auto layout you need to set
customCell?.subtitleLabel?.setTranslatesAutoresizingMaskIntoConstraints(true)
Here you have to take care of some points.
Need to override layoutSubviews() in custom cell, so that you can set frame for that label text
Number of line for label should be zero
Dynamic calculate the height of label
Dynamic cell height
I have created demo for dynamic cell.
Sample demo
Related
I have a UICollectionViewCell prototype which should look like this:
On the left side, there is a UIImageView which I centered vertically in and pinned it to the start of the UICollectionViewCell.
Next comes a title UILabel which I pinned to the top and end of the UICollectionViewCell. I also pinned it to the end of the UIImageView.
Below comes a detail UILabel which I pinned to the bottom of the title, the end of the UICollectionViewCell and constraint it to have the same width as the title.
Below that comes a description UILabel which I pinned to the bottom of the detail, the end of the UICollectionViewCell and the bottom of the UICollectionViewCell. It also has the same width as the title.
What I want to achieve
The title and description labels have 0 number of lines (multiline) because I don't know how many lines the labels will have at runtime.
Via my custom UICollectionViewLayout I provide the target width for the cell. Now I need to figure out, how height the cell must be for the given width.
I have a non visible UICollectionViewCell which I think I can use to calculate the sizes by setting the model from my datasource. But I have a problem.
The Problem
After I set the text of the labels in my "size calculation cell", what do I do with the width and what else do I have to do so I can let the auto layout system calculate a proper height for the cell which I can then return to my UICollectionViewLayout
Or in other words: how can I tell the cell to use the new width to calculate its height.
Add this extension to the String:
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return boundingBox.height
}
}
And then do this:
let heightOfLabel = self.string.height(withConstrainedWidth: labelWidth, font: UIFont(name: "HelveticaNeue", size: 15)!)
You will get the height of the label with that font and that width. After that you can sum up the label heights and give it to the cell.
Okay it seems that I misunderstood how to use sizeThatFits. I now went with a different approach, though. This is what I did to get my self-sizing cells:
I put the three labels in a vertical UIStackView
I put the UIImageView and the UIStackView in a horizontal UIStackView
I pinned to parent stack view to left, right, top and bottom of the cell where the bottom constraint is >= 0 instead of just = 0
I use a protocol in my UICollectionViewLayout to ask for the height of a given width
In my UICollectionViewController, I implement the protocol and where I register my cells, I also instantiate one of the cells which I use for measuring in the protocol function
In the protocol, I first get the model object, then I hand down the model to my measuring cell which I keep in an instance variable in my view controller
I then call getHeight(forWidth) on my cell. In this function, I set the cells frame.size.height to a very large number. Then I use AutoLayout to calculate the actual height (setNeedsDisplay(), layoutIfNeeded())
The height I need is the height of the parent stack view plus the margings
I hope this helps somebody like me in the future. I also wrote a small blog post with some code samples of what I'm doing.
I have a UICollectionView cell, which has multiple items
I need to implement its dynamic height depending on the data of 1st bold label
I have tried multiple code snippets but not working for my case.
as most of them are checking a specific label height and assigning it to the cell size..
Thanks
I have written this function in Xamarin.iOS to calculate the height of cell.
Hope this will help you.
CGRect GetRectOfItemNameLabel(ActivityTaskModel item , UITableView tableView)
{
nfloat width = tableView.Frame.Width;
UIStringAttributes attrs1 = new UIStringAttributes ();
attrs1.Font = UIFont.FromName("Helvetica", 17);
CGRect frameName = ((NSString)item.TaskDescription).GetBoundingRect (new CGSize (width, 100000), NSStringDrawingOptions.UsesLineFragmentOrigin, attrs1, null);
return frameName;
}
Make sure you don't provide any height constraint to that label/TextView.
Note : If your text is larger then rather than using Label use TextView
So I'm trying to use the built-in UITableViewCell styles - specifically UITableViewCellStyleSubtitle - with a (single) line textLabel but multiline detailTextLabel. But the (auto) calculated cell height is consistently too short, and appears to ignore that there is more than 1 line of detail.
I've tried using numberOfLines=0, estimatedRowHeight, UITableViewAutomaticDimension, preferredMaxWidthLayout, etc, but in all the permutations the behavior - indeed for all the UITableViewCell styles - is it appears the UITableViewAutomaticDimension cell height calculation will correctly account for a multiline textLabel (yay!), but incorrectly assumes the detailTextlabel is at most single line (nay!). Consequently, cells with a multiline detailTextLabel are too short, and hence the cell content spills over the top and bottom of the cell.
I've posted a quick test app showing this behavior on GitHub here. Adding additional lines of text is fine - all the cell styles appropriately increase in height to accommodate - but adding additional lines of detail does nothing to change the cell height, and quickly causes the content to spill over; the text+detail are themselves laid out correctly, and together centered correctly over the middle of the cell (so in that sense layoutSubviews is working correctly), but the overall cell height itself is unchanged.
It almost seems like there are no actual top & bottom constraints between the cell.contentView and the labels, and instead the cell height is being calculated directly from the height of the (possibly multi-line) textLabel and (only single-line) detailTextLabel, and then everything is centered over the middle of the cell... Again, multiline textLabel is fine, and I'm doing nothing different between the textLabel and detailTextLabel, but only the former (correctly) adjusts the cell height.
So my question is, if it is possible to use the built-in UITableViewCell styles to reliably display multiline detailTextLabels, or is it simply not possible and you need to create a custom subclass instead? [or, almost equivalently, without having to override layoutSubviews in a subclass and rewire all the constraints manually].
[4 May 2016] Conclusion: as of iOS9 multi-line detailTextLabels dont work as expected with UITableViewAutomaticDimension; the cell will be consistently too short and the text/detail will spill over the top and bottom. Either you must manually compute the correct cell height yourself, or create and layout your own equivalent custom UITableViewCell subclass, or (see my answer below) subclass UITableViewCell and fix systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: to return the correct height [recommended]
Further investigations (see UITableViewCellTest) indicate that when UITableViewAutomaticDimension is enabled the system calls -systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: to calculate the cell height, and that this pretty much ignores the height of the detailTextLabel in its computation (bug !?). As a result, for UITableViewCellStyleSubtitle the cell height is always going to be too short [a single-line detailTextLabel may not quite spill over the cell, but that's only because of the existing top and bottom margins], and for UITableViewCellStyleValue1 or UITableViewCellStyleValue2 the height will be too short whenever the detailTextLabel is taller (eg more lines) than the textLabel. This is all a moot point for UITableViewCellStyleDefault which has no detailTextLabel.
My solution was to subclass and fix with:
- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize
withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority
verticalFittingPriority:(UILayoutPriority)verticalFittingPriority
{
// Bug finally fixed in iOS 11
if ([UIDevice.currentDevice.systemVersion compare:#"11" options:NSNumericSearch] != NSOrderedAscending) {
return [super systemLayoutSizeFittingSize:targetSize
withHorizontalFittingPriority:horizontalFittingPriority
verticalFittingPriority:verticalFittingPriority];
}
[self layoutIfNeeded];
CGSize size = [super systemLayoutSizeFittingSize:targetSize
withHorizontalFittingPriority:horizontalFittingPriority
verticalFittingPriority:verticalFittingPriority];
CGFloat detailHeight = CGRectGetHeight(self.detailTextLabel.frame);
if (detailHeight) { // if no detailTextLabel (eg style = Default) then no adjustment necessary
// Determine UITableViewCellStyle by looking at textLabel vs detailTextLabel layout
if (CGRectGetMinX(self.detailTextLabel.frame) > CGRectGetMinX(self.textLabel.frame)) { // style = Value1 or Value2
CGFloat textHeight = CGRectGetHeight(self.textLabel.frame);
// If detailTextLabel taller than textLabel then add difference to cell height
if (detailHeight > textHeight) size.height += detailHeight - textHeight;
} else { // style = Subtitle, so always add subtitle height
size.height += detailHeight;
}
}
return size;
}
And in the view controller:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.estimatedRowHeight = 44.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
You can pull the full subclass from here: MultilineTableViewCell
So far this fix appears to work well, and has let me successfully use the built-in UITableViewCellStyles with multiline text and details, in self-sizing cells with dynamic type support. This avoids the trouble (and mess) of manually computing the desired cell heights in tableView:heightForRowAtIndexPath:, or having to create custom cell layouts.
[(PARTLY)FIXED IN iOS11]
Apple finally fixed this bug in iOS11 (but apparantly only for UITableViewCellStyleSubtitle). I've updated my solution to only apply the necessary correction to pre-11 devices (otherwise you'll end up with extra space top and bottom of your cell!).
#tiritea 's answer in Swift 3 (Thanks again! :D)
// When UITableViewAutomaticDimension is enabled the system calls
// -systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority: to calculate the cell height.
// Unfortunately, it ignores the height of the detailTextLabel in its computation (bug !?).
// As a result, for UITableViewCellStyleSubtitle the cell height is always going to be too short.
// So we override to include detailTextLabel height.
// Credit: http://stackoverflow.com/a/37016869/467588
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
self.layoutIfNeeded()
var size = super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
if let textLabel = self.textLabel, let detailTextLabel = self.detailTextLabel {
let detailHeight = detailTextLabel.frame.size.height
if detailTextLabel.frame.origin.x > textLabel.frame.origin.x { // style = Value1 or Value2
let textHeight = textLabel.frame.size.height
if (detailHeight > textHeight) {
size.height += detailHeight - textHeight
}
} else { // style = Subtitle, so always add subtitle height
size.height += detailHeight
}
}
return size
}
It looks like Apple has resolved this bug in iOS 11.
Swift 3
After reading various answers, I have used following method for get ride of detail text label UITableViewAutomaticDimension issue . Use Basic style cell with title label only and use attributed string for Text and detail text view. Don't forget to Change tableview cell style from Subtitle to Basic.
func makeAttributedString(title: String, subtitle: String) -> NSAttributedString {
let titleAttributes = [NSFontAttributeName: UIFont.preferredFont(forTextStyle: .headline), NSForegroundColorAttributeName: UIColor.purple]
let subtitleAttributes = [NSFontAttributeName: UIFont.preferredFont(forTextStyle: .subheadline)]
let titleString = NSMutableAttributedString(string: "\(title)\n", attributes: titleAttributes)
let subtitleString = NSAttributedString(string: subtitle, attributes: subtitleAttributes)
titleString.append(subtitleString)
return titleString
}
How to use in cellforrowatindexpath
cell.textLabel?.attributedText = makeAttributedString(title: "Your Title", subtitle: "Your detail text label text here")
Add Following lines in viewdidload
YourTableView.estimatedRowHeight = UITableViewAutomaticDimension
YourTableView.rowHeight = UITableViewAutomaticDimension
YourTableView.setNeedsLayout()
YourTableView.layoutIfNeeded()
From my experience the built in cells don't support auto resize with constraints, I think the best solution is to create a custom cell, it really takes a couple of minutes and you don't need to override layoutSubview, it is really simple .
Just change the type of the cell in the IB to custom, drag a label , set constraints (in the IB), set number of rows , create a subclass, change the cells class in the IB to your subclass, create an outlet in the subclass and that's most of the work,
I am sure there are a lot of tutorials on the net you can follow.
I am aware that this question has been asked, but none of the answers have worked for me. I'm trying to implement a comments View controller, similar to what you can see in Instagram, where the size of the tableView cell depends on the size of the comment. So I though I would get the necessary height to display the whole comment in textView without scrolling, adjust the textView, then use it to set the heightForRowAtIndexPath appropriately, before finally reloading the table. However, I can't even get to resize the textView, I have tested a certain number of answers and still the textView won't budge.
sizeToFit and pinning to superview didn't work.
Any ideas on how I can do this?
Thanks for your help
Best way to approach this is to calculate the size of your tableviewcell based on the size of the string you're wanting to display.
Don't use the size of the textView because it doesn't really know its contents until it gets displayed.
Try this to get the height of the string:
func calculateHeightForString(inString:String) -> CGFloat
{
var messageString = inString
var attributes = [UIFont(): UIFont.systemFontOfSize(15.0)]
var attrString:NSAttributedString? = NSAttributedString(string: messageString, attributes: attributes)
var rect:CGRect = attrString!.boundingRectWithSize(CGSizeMake(300.0,CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil )
var requredSize:CGRect = rect
return requredSize.height //to include button's in your tableview
}
taken from the top answer here: Dynamically size uitableViewCell according to UILabel (With paragraph spacing)
Then all you need to do is add the padding to that height result for your tableviewcell.
As long as your textview is setup with the correct constraints by pinning it to all 4 sides of the tableViewCells content view, everything should work out fine.
I'm trying to setup a tableview which each cell will have an Image on the left, a Label which overlays the image, and finally a label to the right of the image with long text in which I would like for it wrap to the next line if needed. My tableview row height is set at 65.
I have set the number of lines to 0 and set the line break to work wrap.
I even tried setting parameter programmatically in my CustomTableViewCell class:
self.materialLabel?.numberOfLines = 0
self.materialLabel.sizeToFit()
self.materialLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
I've tried many combinations of setting constraints on my label, but it either doesn't work or affects all objects in the cell with the image and image label out of sync. The alignment constraints are not available to set.
Working with Xcode 6.3.2, Swift 1.2, and iOS 8
Thanks in Advanced!
You can use dynamic cell height ( or self sizing cell).
Basically, you create top, leading, bottom, trailing label's constraints relative to cell's contentView.
and then set
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = someValue
http://www.raywenderlich.com/87975/dynamic-table-view-cell-height-ios-8-swift
There are many ways to setup dynamic height for table cell.
If your not not using autolayout, you need to calculate table size programmatically and return using table view delegate method.
If your using autolayout, your life will be very easy. Add constraints to cell, I.e add border, width and height constraints to image. Add only 4 border constraints to image(don't add height constraint). This may not be the exact constraints, but this will give you a idea. Add following code in viewDidload
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = someValue
This should work.
Turns out my label width was too wide. I had to anchor my imageview and the imagelabel and set the width and height before setting the constraints on my label.