'How to calculate height of a String?

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



Solution 1:[1]

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

Solution 2:[2]

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)")

Solution 3:[3]

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
}

Solution 4:[4]

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))

Solution 5:[5]

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;

Solution 6:[6]

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.

Enable AutoLayout

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

Solution 7:[7]

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)
    }}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Chetan Purohit
Solution 3
Solution 4 Ganesh Manickam
Solution 5 kalpesh
Solution 6
Solution 7 Jawad Ali