简体   繁体   中英

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!

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 .

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.

在此输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM