Check truncation in UILabel - iOS, Swift

I am working on a quick application. I'm currently working on a collection of tabular views with custom cells, see screenshot . However, right now I have a text set, so the title has exactly 2 lines and the summary has exactly 3 lines. By doing this, the text is sometimes truncated. Now I want to set the priority for the text in the title, so if the title is truncated when it lasts 2 lines, I expand it to 3 lines and make a summary of only two lines. I tried to do this with auto-layout, but failed. Now I tried the following approach, in accordance with this and this , but the function below also did not appear to determine exactly whether the text is truncated.

func isTruncated(label:UILabel) -> Bool { let context = NSStringDrawingContext() let text : NSAttributedString = NSAttributedString(string: label.text!, attributes: [NSFontAttributeName : label.font]) let labelSize : CGSize = CGSize(width: label.frame.width, height: CGFloat.max) let options : NSStringDrawingOptions = unsafeBitCast(NSStringDrawingOptions.UsesLineFragmentOrigin.rawValue | NSStringDrawingOptions.UsesFontLeading.rawValue, NSStringDrawingOptions.self) let labelRect : CGRect = text.boundingRectWithSize(labelSize, options: options, context: context) if Float(labelRect.height/label.font.lineHeight) > Float(label.numberOfLines) { return true } else { return false } } 

Can anyone help? How can I change my function to make this work? Or should it work with various auto-layout limitations and how? Thank you very much!


EDIT: this is my current code. Some automatic layout is done, this is a storyboard, however, changing the automatic layout is done in code. import UIKit

 class FeedTableViewCell: UITableViewCell { var thumbnailImage = UIImageView() @IBOutlet var titleText: UILabel! @IBOutlet var summaryText: UILabel! @IBOutlet var sourceAndDateText: UILabel! var imgTitleConst = NSLayoutConstraint() var imgSummaryConst = NSLayoutConstraint() var imgDetailConst = NSLayoutConstraint() var titleConst = NSLayoutConstraint() var summaryConst = NSLayoutConstraint() var detailConst = NSLayoutConstraint() var titleHeightConst = NSLayoutConstraint() var summaryHeightConst = NSLayoutConstraint() var imageRemoved = false var titleConstAdd = false override func awakeFromNib() { super.awakeFromNib() thumbnailImage.clipsToBounds = true summaryText.clipsToBounds = true titleText.clipsToBounds = true sourceAndDateText.clipsToBounds = true addImage() } func removeImage() { if let viewToRemove = self.viewWithTag(123) { imageRemoved = true viewToRemove.removeFromSuperview() self.contentView.removeConstraints([imgTitleConst, imgSummaryConst, imgDetailConst]) titleConst = NSLayoutConstraint(item: self.titleText, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 14) summaryConst = NSLayoutConstraint(item: summaryText, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 14) detailConst = NSLayoutConstraint(item: sourceAndDateText, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 14) self.contentView.addConstraints([titleConst, detailConst, summaryConst]) setNumberOfLines() self.contentView.layoutSubviews() } } func addImage() { thumbnailImage.tag = 123 thumbnailImage.image = UIImage(named: "placeholder") thumbnailImage.frame = CGRectMake(14, 12, 100, 100) thumbnailImage.contentMode = UIViewContentMode.ScaleAspectFill thumbnailImage.clipsToBounds = true self.contentView.addSubview(thumbnailImage) if imageRemoved { self.contentView.removeConstraints([titleConst, summaryConst, detailConst]) } var widthConst = NSLayoutConstraint(item: thumbnailImage, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100) var heightConst = NSLayoutConstraint(item: thumbnailImage, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100) var leftConst = NSLayoutConstraint(item: thumbnailImage, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 14) var topConst = NSLayoutConstraint(item: thumbnailImage, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 12) imgTitleConst = NSLayoutConstraint(item: self.titleText, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.thumbnailImage, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 8) imgSummaryConst = NSLayoutConstraint(item: summaryText, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.thumbnailImage, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 8) imgDetailConst = NSLayoutConstraint(item: sourceAndDateText, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.thumbnailImage, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 8) self.contentView.addConstraints([widthConst, heightConst, leftConst, topConst, imgTitleConst, imgSummaryConst, imgDetailConst]) setNumberOfLines() self.contentView.layoutSubviews() } override func setSelected(selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state } func setNumberOfLines() { if titleConstAdd { self.contentView.removeConstraints([titleHeightConst, summaryHeightConst]) } if titleText.numberOfLines == 3 { titleText.numberOfLines = 2 } if countLabelLines(titleText) > 2 { titleText.numberOfLines = 3 summaryText.numberOfLines = 2 println("adjusting label heigh to be taller") titleHeightConst = NSLayoutConstraint(item: titleText, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 51) summaryHeightConst = NSLayoutConstraint(item: summaryText, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 32) self.contentView.addConstraints([titleHeightConst, summaryHeightConst]) } else { titleText.numberOfLines = 2 summaryText.numberOfLines = 3 titleHeightConst = NSLayoutConstraint(item: titleText, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 36) summaryHeightConst = NSLayoutConstraint(item: summaryText, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 47) self.contentView.addConstraints([titleHeightConst, summaryHeightConst]) } titleConstAdd = true } } func countLabelLines(label:UILabel)->Int{ if let text = label.text{ // cast text to NSString so we can use sizeWithAttributes var myText = text as NSString //Set attributes var attributes = [NSFontAttributeName : UIFont.boldSystemFontOfSize(14)] //Calculate the size of your UILabel by using the systemfont and the paragraph we created before. Edit the font and replace it with yours if you use another var labelSize = myText.boundingRectWithSize(CGSizeMake(label.bounds.width, CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: attributes, context: nil) //Now we return the amount of lines using the ceil method var lines = ceil(CGFloat(labelSize.height) / label.font.lineHeight) println(labelSize.height) println("\(lines)") return Int(lines) } return 0 } 
+6
source share
3 answers

You can use the sizeWithAttributes method from NSString to get the number of rows your UILabel . First, you will need to label the text on the NSString to use this method:

 func countLabelLines(label:UILabel)->Int{ if let text = label.text{ // cast text to NSString so we can use sizeWithAttributes var myText = text as NSString //A Paragraph that we use to set the lineBreakMode. var paragraph = NSMutableParagraphStyle() //Set the lineBreakMode to wordWrapping paragraph.lineBreakMode = NSLineBreakMode.ByWordWrapping //Calculate the size of your UILabel by using the systemfont and the paragraph we created before. Edit the font and replace it with yours if you use another var labelSize = myText.sizeWithAttributes([NSFontAttributeName : UIFont.systemFontOfSize(17), NSParagraphStyleAttributeName : paragraph.copy()]) //Now we return the amount of lines using the ceil method var lines = ceil(CGFloat(size.height) / label.font.lineHeight) return Int(lines) } return 0 } 

Edit

If this method does not work for you because your label does not have a fixed width, you can use boundingRectWithSize to get the size of the label and use it with the ceil method.

 func countLabelLines(label:UILabel)->Int{ if let text = label.text{ // cast text to NSString so we can use sizeWithAttributes var myText = text as NSString //Set attributes var attributes = [NSFontAttributeName : UIFont.systemFontOfSize(UIFont.systemFontSize())] //Calculate the size of your UILabel by using the systemfont and the paragraph we created before. Edit the font and replace it with yours if you use another var labelSize = myText.boundingRectWithSize(CGSizeMake(label.bounds.width, CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: attributes, context: nil) //Now we return the amount of lines using the ceil method var lines = ceil(CGFloat(labelSize.height) / label.font.lineHeight) return Int(lines) } return 0 } 
+10
source

Swift 3

A simple solution counts the number of lines after line assignment and compares with the maximum number of label lines.

 import Foundation import UIKit extension UILabel { func countLabelLines() -> Int { // Call self.layoutIfNeeded() if your view is uses auto layout let myText = self.text! as NSString let attributes = [NSFontAttributeName : self.font] let labelSize = myText.boundingRect(with: CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes, context: nil) return Int(ceil(CGFloat(labelSize.height) / self.font.lineHeight)) } func isTruncated() -> Bool { if (self.countLabelLines() > self.numberOfLines) { return true } return false } } 
+4
source

This works for labels with a fixed width and a fixed number of lines or a fixed height:

 extension UILabel { func willBeTruncated() -> Bool { let label:UILabel = UILabel(frame: CGRectMake(0, 0, self.bounds.width, CGFloat.max)) label.numberOfLines = 0 label.lineBreakMode = NSLineBreakMode.ByWordWrapping label.font = self.font label.text = self.text label.sizeToFit() if label.frame.height > self.frame.height { return true } return false } } 
+2
source

Source: https://habr.com/ru/post/983483/


All Articles