检查 UILabel 中的截断 - iOS、Swift

Check for Truncation in UILabel - iOS, Swift

我正在 swift 开发应用程序。目前,我正在使用自定义单元格处理 table 视图的填充,请参阅 screenshot. However, right now I have the text set so that the title is exactly 2 lines and the summary is exactly 3 lines. By doing this, the text is sometimes truncated. Now, I want to set the priority for text in the title, so that if the title is truncated when it is 2 lines long I expand it to 3 lines and make the summary only 2 lines. I tried doing this with auto layout, but failed. Now I tried the following approach, according to this and this,但下面的函数似乎也无法准确确定文本是否被截断。

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

有人可以帮忙吗?我怎样才能改变我的功能来完成这项工作?或者应该使用不同的自动布局约束以及如何工作? 非常感谢!


编辑: 这是我当前的代码。一些自动布局是在故事板中完成的,但是更改自动布局是在代码中完成的。 导入 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

}

您可以使用 NSString 中的 sizeWithAttributes 方法来获取 UILabel 的行数。您必须先将标签文本转换为 NSString 才能使用此方法:

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

}

编辑

如果由于您的标签没有固定宽度,此方法对您不起作用,您可以使用 boundingRectWithSize 获取标签的大小,并将其与 ceil 方法一起使用.

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

}

这适用于具有固定宽度和固定行数或固定高度的标签:

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

Swift 3

一个简单的解决方案是在分配字符串后计算行数并与标签的最大行数进行比较。

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