Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting UILabel height with multiple lines in Swift

I'm trying to calculate the height of my UILabel to make the page scrollable. The label contains attributedText to display HTML content.

I'm using this function to get my HTML content to the UILabel:

func stringFromHTML( string: String?) -> NSAttributedString
{
    do{
        let pStyle = NSMutableParagraphStyle()
        pStyle.lineSpacing = 4
        pStyle.paragraphSpacingBefore = 10

        let str = try NSMutableAttributedString(data:string!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true
            )!, options:[NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSNumber(unsignedLong: NSUTF8StringEncoding)], documentAttributes: nil)
        str.addAttribute(NSParagraphStyleAttributeName, value: pStyle, range: NSMakeRange(0, str.length))
        str.addAttribute(NSFontAttributeName, value: UIFont(name: "Helvetica Neue", size: 16.0)!, range: NSMakeRange(0, str.length))

        return str
    } catch
    {
        print("html error\n",error)
    }
    return NSAttributedString(string: "")
}

I'm a newbie. So, I make a research and find this extention to get the height of the UILabel. It returns a value but I guess it's not true. My UIScrollView doesn't seem to work:

extension UILabel{
    func requiredHeight() -> CGFloat{
        let label:UILabel = UILabel(frame: CGRectMake(0, 0, self.frame.width, CGFloat.max))
        label.numberOfLines = 0
        label.lineBreakMode = NSLineBreakMode.ByWordWrapping
        label.font = self.font
        label.text = self.text

        label.sizeToFit()

        return label.frame.height
    }
}

And this is my code in the viewDidLoad() method for the constraints:

// Set ContentView height
let activityTitleHeight:CGFloat = self.activityTitle.requiredHeight() + 20
let activityDescHeight:CGFloat = self.activityDesc.requiredHeight() + 20
let totalContentHeight:CGFloat = activityDescHeight + activityTitleHeight + 345
// There is an image view and a navigation, their total height: 345

let contentViewHeight:NSLayoutConstraint = NSLayoutConstraint(item: self.contentView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: totalContentHeight)
self.contentView.addConstraint(contentViewHeight)

Thanks in advance!

like image 416
Mert Diricanlı Avatar asked Mar 11 '23 05:03

Mert Diricanlı


2 Answers

Since you want entire page to become scrollable once the HTML content is larger than the size of the application's window, I presume your top level view is UIScrollView.

The main trick here is to set height constraint of the label displaying the HTML to 0 and then update it programmatically once you calculate the height of the HTML text it contains.

If the text being displayed is too long, then the scroll view will start scrolling automatically provided that you set all vertical constraints correctly.

By the way, it is an overkill to create a UILabel just to calculate the height of a string. As of iOS7, we have NSAttributedString.boundingRect(size:options:context) method which calculates the minimum rect needed for drawing the text with given options:

string.boundingRectWithSize(CGSizeMake(width, CGFloat.max), options: [.UsesFontLeading, .UsesLineFragmentOrigin], context: nil)

Going back to your question, you should create an ivar of NSLayoutConstraint type representing the height of the label in question:

class ViewController: UIViewController {
  @IBOutlet weak var htmlLabel: UILabel!
  private var labelHeightConstraint: NSLayoutConstraint!
  ...

and set its constant property to 0 as we said earlier:

override func viewDidLoad() {
  super.viewDidLoad()
  ...

  htmlLabel.text = stringFromHTML(htmlText)

  labelHeightConstraint = NSLayoutConstraint(item: htmlLabel, attribute: .Height, relatedBy: .Equal,
                                             toItem: nil, attribute: .NotAnAttribute, multiplier: 0, constant: 0)
  scrollView.addConstraint(labelHeightConstraint)
}

Then calculate the height of the label:

(Since we don't have the exact width of any superview in viewDidLoad, viewDidAppear is a good place to do such calculation.)

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)

  let maxSize = CGSizeMake(CGRectGetWidth(scrollView.frame), CGFloat.max)
  let textSize = htmlLabel.attributedText!.boundingRectWithSize(maxSize, options: [.UsesFontLeading, .UsesLineFragmentOrigin], context: nil)

  labelHeightConstraint.constant = ceil(textSize.height)
}

I created a test project for you showing what I did above. You can download it from here.

like image 63
Ozgur Vatansever Avatar answered Mar 20 '23 00:03

Ozgur Vatansever


It's better way to put such a text into the UITextView. This element serves as a text container with:

  • multiline support
  • attributed support
  • regardless length of the text it fits to the view as it work like scrollview

.

let textField = UITextField()
textField.text = "Your long text from HTML source ..."
textField.frame = CGRect(x: 0, y: 0, width: 375, height: 200)
self.contentView.addSubview(textField)

Even if you don't use storyboard, I post just a small screenshot, what attributes this UI element brings to the place.

enter image description here

like image 41
pedrouan Avatar answered Mar 19 '23 23:03

pedrouan