How to calculate height of a String? - ios

I am trying to adjust the cell height resize to fit the UILabel text, but it is not working..
var mySize = CGFloat()
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! cellView
cell.myLabel.text = self.items[indexPath.item]
cell.myLabel.bounds.size.height = self.mySize
cell.backgroundColor = UIColor.yellowColor()
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
func heightForLabel(text:String, font:UIFont, width:CGFloat) -> CGFloat
{
let label:UILabel = UILabel(frame: CGRectMake(0, 0, width, CGFloat.max))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
label.font = font
label.text = items[indexPath.row]
label.sizeToFit()
return label.frame.height
}
let font = UIFont(name: "Helvetica Neue", size: 30)
let detailHeight = heightForLabel(items[indexPath.row], font: font!, width: UIScreen.mainScreen().bounds.size.width)
self.mySize = detailHeight
return CGSizeMake(UIScreen.mainScreen().bounds.size.width, 358 + detailHeight)
}
Any suggestions what to do here? Should i do it another way? Please, I need help.. The problem is that the UILabel text is set in the cellForItemAtIndexPath, and items is an array for strings.
This is my project file, if someone watch to take a look at it:
http://www.filedropper.com/test_37

Why not try this in ObjC
[text boundingRectWithSize:CGSizeMake(maxWidth, maxHeight)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:nil context:nil]
This will give CGRect. Get the height from it. set font size etc in attributes parameter.
UPDATE
In place of this
let detailHeight = heightForLabel(items[indexPath.row], font: font!, width: UIScreen.mainScreen().bounds.size.width)
Use this
let height = items[indexPath.row].boundingRectWithSize(CGSizeMake(CGFloat.max,UIScreen.mainScreen().bounds.size.width), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font!], context: nil).size.height
Hope this helps

You can do it like that but I have implemented it in different way. Here is the sample of code you can do it.
let desiredWidth: CGFloat = tableView.bounds.size.width
let label: UILabel = UILabel()
let desiredString = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged."
label.text = desiredString
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
let size: CGSize = label.sizeThatFits(CGSizeMake(desiredWidth, CGFloat.max))
print("Label height you can set to your cell: \(size.height)")

Crate an String extension
extension String {
func heightOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSFontAttributeName: font]
let size = self.size(attributes: fontAttributes)
return size.height
}
}
get the height of the string as follows
let str = "Hello world"
let strHgt = str.heightOfString(usingFont: UIFont.systemFont(ofSize: 12))

I create this method for getting height of a label. You need to provide label's static Width and label's font
func dynamicHeight(font: UIFont, width: CGFloat) -> CGFloat{
let calString = NSString(string: self)
let textSize = calString.boundingRectWithSize(CGSizeMake(width, CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin|NSStringDrawingOptions.UsesFontLeading, attributes: [NSFontAttributeName: font], context: nil)
return textSize.height
}

Try this...
NSString *yourText = #"Your string";
CGSize lableWidth = CGSizeMake(300, CGFLOAT_MAX);
CGSize requiredSize = [yourText sizeWithFont:[UIFont fontWithName:#"CALIBRI" size:17] constrainedToSize:lableWidth lineBreakMode:NSLineBreakByWordWrapping];
int calculatedHeight = requiredSize.height;
return (float)calculatedHeight;

I took a look at your code, and I was able to solve it.
Firstly, on line 71 in your ViewController class:
let height = items[indexPath.row].boundingRectWithSize(CGSizeMake(CGFloat.max, UIScreen.mainScreen().bounds.size.width), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font!], context: nil).size.height
You accidentally set CGFloat.max as width and the width as height. It should be:
CGSizeMake(UIScreen.mainScreen().bounds.size.width, CGFloat.max)
I'd say it's better practice to use the width of the view that the cell is directly contained in (the collectionView), but that's just my personal opinion.
Secondly, you need to enable AutoLayout. Go to your Main.storyboard file and make sure Use Auto Layout is selected.
Now you need to add constraints. (You can read more about AutoLayout and constraints here)
There are different ways to add constraints. The easiest way is to control click a UI element and drag the mouse to the element you want to set a constraint to.
You need to add the following constraints for your cell:
ImageView.top = cell.top
ImageView.leading = cell.leading
ImageView.trailing = cell.trailing
ImageView.bottom = MyLabel.top + 8 (your padding)
MyLabel.leading = cell.leading
MyLabel.trailing = cell.trailing
MyLabel.bottom = cell.bottom
And these for your CollectionView
CollectionView.top = view.top
CollectionView.leading = view.leading
CollectionView.trailing = view.trailing
CollectionView.bottom = view.bottom
I've attached the project, modified with AutoLayout here below.
Modified project
Edit:
Approach 2 - without AutoLayout.
This could also be achieved without using AutoLayout by manually updating the cell's label height in collectionView:willDisplayCell:. I'm sure there are better alternatives, I'd personally try AutoResizingMasks before this approach.
Project without AutoLayout

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 ceil(boundingBox.height)
}}

Related

How to get the content Height of dynamically changing Text in textview?

I have a dynamically changing text in TextView.I could not be able to get the content Height of TextView.
Here is what i tried.
let height = self.tvComment.contentSize.height
print("height",height)
let contentSizeComment = self.tvComment.sizeThatFits(self.tvComment.bounds.size)
print("height",contentSizeComment)
Why it's not getting the content height of TextView?
Hope you understand my problem.
Thanks in Advance
Usethis method to get the height -
func heightForString(text:String, font:UIFont, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.size.height
}
in textdidchange you can use this code,in order to resize when textchange
//approxi should be the width of your textview
let approxi = view.frame.width - 90
//size is the max width and height of textview,1000 can be what ever you want
let size = CGSize(width: approxi, height: 1000)
//dont forget to put your font and size
//chey is the text of thetext view
let attributes = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 15)]
let estim = NSString(string: chey).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
//estim is height
above it the first method,second method will come in edit
second method is
func pva() {
//what was the name of my textfield
let fixedwidth = what.frame.size.width - 40
let newsize = what.contentSize.height
self.hrightext.constant = newsize
self.view.layoutIfNeeded()
}
note: both are tested and works in swift4
Your just write code in below delegate of UITextView:-
func textViewDidChange(_ textView: UITextView!) {
let height = self.tvComment.contentSize.height
print("height",height)
let contentSizeComment = self.tvComment.sizeThatFits(self.tvComment.bounds.size)
print("height",contentSizeComment)
}
I hope it help you,
Thank you.

get height of dynamic textViews in tableView without using automatic dimensions using swift

I want to calculate the height of my textView's in a tableView dynamically to use in heightForRowAt. I DO NOT want to use automatic dimensions as it often messes up my scrolling in containerViews.
Presently I am instantiating a textView for every cell, adding the text and getting the height using:
var textViewForCellHeight = UITextView()
textViewForCellHeight.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
textViewForCellHeight.frame = CGRect(x: 0, y: 0, width: self.tableView.frame.size.width - cellHorizontalPadding - tableView.safeAreaInsets.left - tableView.safeAreaInsets.right, height: 0)
textViewForCellHeight.text = myString
textViewForCellHeight.sizeToFit()
return textViewForCellHeight.frame.size.height
Using this in heightForRowAt works fine and gives the correct height for the cell, but it expensive and slows down the tableView considerably. Is there a more efficient way to get the height of the tableView cell's dynamically with a textView?
You can simply pass your string in this function and you will get dynamic height according to your string.
func calculateHeight(inString:String) -> CGFloat
{
let messageString = input.text
let attributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue) : UIFont.systemFont(ofSize: 15.0)]
let attributedString : NSAttributedString = NSAttributedString(string: messageString!, attributes: attributes)
let rect : CGRect = attributedString.boundingRect(with: CGSize(width: 222.0, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)
let requredSize:CGRect = rect
return requiredSize.height
}
Try this code:
func cellHeight(withTxt string: String) -> Float {
let textRect: CGRect = string.boundingRect(with: CGSize(width: self.view.frame.width/* Preferred textView Width */, height: CGFloat(MAXFLOAT)), options: ([.usesLineFragmentOrigin, .usesFontLeading]), attributes: [.font: UIFont(name: "Helvetica Neue", size: 17)!], context: nil)
let requiredSize: CGSize = textRect.size
//finally u return your height
return Float(requiredSize.height)
}

How to change detailTextLabel height programmatically

I programmed an array into a tableView. When the cell utilizes more than one line for the detailTextLabel, the space in between the lines is small. I would like to know if there is any way to increase this height programmatically? Here is sample code I am using for the array.
cell.textLabel?.text = self.filtered[indexPath.row].coptic
cell.detailTextLabel?.text = self.filtered[indexPath.row].english
cell.textLabel?.font = UIFont(name:"CS Avva Shenouda", size:30)
cell.detailTextLabel?.font = UIFont(name: "Constantia", size:25)
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
cell.detailTextLabel?.textColor = UIColor.darkGray
return cell
I'm just putting my logic, not whole code.
You can get height of string by below code
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 ceil(boundingBox.height)
}
Change cellForRowAtIndexPath Method
cell.detailTextLabel?.numberOfLines = 0
let height = height(withConstrainedWidth:200, font:YourFont) // change width and font as per your requirement
cell.detailTextLabel?.frame = CGRect(x: cell.detailTextLabel?.frame.origin.x, y: cell.detailTextLabel?.frame.origin.y, width: cell.detailTextLabel?.frame.size.width, height: height)
You can manage cell height according to detailTextLabel height
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 50 // You should put your code or logic that dynamic height based on heigh of label.
}
The table view needs to have an estimatedRowHeight and the tableView height as UITableViewAutomaticDimension.

Swift : boundingRectWithSize return wrong height for a multiline label

I'm using an extension on the String class to get the height needed to display my string in a multiline label.
extension String {
func heightWithConstrainedWidth(width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: CGFloat.max)
let option = NSStringDrawingOptions.UsesFontLeading.union(.UsesLineFragmentOrigin)
let boundingBox = self.boundingRectWithSize(constraintRect, options: option, attributes: [NSFontAttributeName: font], context: nil)
print("ex width \(boundingBox.width)")
print("ex height \(boundingBox.height)")
return boundingBox.height
}
}
I'm calling it in heightForRowAtIndexPath
let myRandomString = "My baby looks like she's ashamed and wants to apologise for something bad she did."
let labelWidth = UIScreen.mainScreen().bounds.width-16
let labelHeight = myRandomString.heightWithConstrainedWidth(labelWidth,font: UIFont.boldSystemFontOfSize(17))
and I get :
ex width 359.0
ex height 40.57421875
But when I use the view debuging option of xcode I get this :
And this is how my UILabel is setup :
As you can see my width is correct (I'm forcing it) but my height is incorrect and I can't figure out why.
Thanks and regards,
Eric
For multiline only use .UsesLineFragmentOrigin.

My return of height of a UILabel is never accurate

I'm trying to capture the height of my UILabel so that I can dynamically set the height of it's cell, but it never returns the right height, and effectually, my content is always truncated and appended with ellipses.
My code :
let height = String(page.valueForKey(subject)).heightWithConstrainedWidth(self.view.frame.width - 30, font: UIFont.systemFontOfSize(16.0))
// and at the bottom of my class..
// Custom functions
extension String {
func heightWithConstrainedWidth(width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: CGFloat.max)
let boundingBox = self.boundingRectWithSize(constraintRect, options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return boundingBox.height
}
}
It definitely makes a valiant attempt because it returns some sort of dynamic number that is relatively large, but the number is always short and never accurate.
Is there something glaringly inapropriate about how I'm extracting height? Is there a better way to perform this?
Per Matt's response, I updated to measure the label as so :
// Custom functions
extension String {
func heightWithConstrainedWidth(width: CGFloat, font: UIFont) -> CGFloat {
let label:UILabel = UILabel(frame: CGRectMake(0,0,width, CGFloat.max))
label.numberOfLines = 0
label.lineBreakMode = .ByWordWrapping
label.font = font
label.text = self
label.sizeToFit()
return label.frame.height
}
}
But it's still not quite big enough..
Your code is fine for what it does. The problem is merely that a label is not a string. You are finding the height of a string. That isn't what you want to do. You want to find the height of a label! There is more to a UILabel, after all, than just the string it contains. The UILabel is taller than the string. You are not taking that into account.

Resources