我试图计算基于不同字符串长度的UILabel的高度。
func calculateContentHeight() -> CGFloat{
var maxLabelSize: CGSize = CGSizeMake(frame.size.width - 48, CGFloat(9999))
var contentNSString = contentText as NSString
var expectedLabelSize = contentNSString.boundingRectWithSize(maxLabelSize, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont.systemFontOfSize(16.0)], context: nil)
print("\(expectedLabelSize)")
return expectedLabelSize.size.height
}
上面是我用来确定高度的当前函数,但它不起作用。如果能得到任何帮助,我将不胜感激。我更喜欢Swift的答案,而不是Objective C。
在String上使用扩展
斯威夫特3
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)
}
func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.width)
}
}
还有NSAttributedString(有时非常有用)
extension NSAttributedString {
func height(withConstrainedWidth width: CGFloat) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)
return ceil(boundingBox.height)
}
func width(withConstrainedHeight height: CGFloat) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)
return ceil(boundingBox.width)
}
}
Swift 4 & 5
只需更改扩展String方法中的属性值
from
[NSFontAttributeName: font]
to
[.font : font]
extension String{
func widthWithConstrainedHeight(_ height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.width)
}
func heightWithConstrainedWidth(_ width: CGFloat, font: UIFont) -> CGFloat? {
let constraintRect = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.height)
}
}
这是我在Swift 4.1和Xcode 9.4.1中的答案
//This is your label
let proNameLbl = UILabel(frame: CGRect(x: 0, y: 20, width: 300, height: height))
proNameLbl.text = "This is your text"
proNameLbl.font = UIFont.systemFont(ofSize: 17)
proNameLbl.numberOfLines = 0
proNameLbl.lineBreakMode = .byWordWrapping
infoView.addSubview(proNameLbl)
//Function to calculate height for label based on text
func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat {
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
现在你调用这个函数
//Call this function
let height = heightForView(text: "This is your text", font: UIFont.systemFont(ofSize: 17), width: 300)
print(height)//Output : 41.0
我发现公认的答案适用于固定的宽度,但不适用于固定的高度。对于固定的高度,它只会增加宽度以适应一行中的所有内容,除非文本中有换行符。
width函数多次调用height函数,但这是一个快速计算,在UITable的行中使用该函数时,我没有注意到性能问题。
extension String {
public func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font : font], context: nil)
return ceil(boundingBox.height)
}
public func width(withConstrainedHeight height: CGFloat, font: UIFont, minimumTextWrapWidth:CGFloat) -> CGFloat {
var textWidth:CGFloat = minimumTextWrapWidth
let incrementWidth:CGFloat = minimumTextWrapWidth * 0.1
var textHeight:CGFloat = self.height(withConstrainedWidth: textWidth, font: font)
//Increase width by 10% of minimumTextWrapWidth until minimum width found that makes the text fit within the specified height
while textHeight > height {
textWidth += incrementWidth
textHeight = self.height(withConstrainedWidth: textWidth, font: font)
}
return ceil(textWidth)
}
}
这里有一个简单的解决方案,对我来说很有用。类似于其他发布的一些,但它不需要sizeToFit
注意这是用Swift 5写的
let lbl = UILabel()
lbl.numberOfLines = 0
lbl.font = UIFont.systemFont(ofSize: 12) // make sure you set this correctly
lbl.text = "My text that may or may not wrap lines..."
let width = 100.0 // the width of the view you are constraint to, keep in mind any applied margins here
let height = lbl.systemLayoutSizeFitting(CGSize(width: width, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel).height
它处理行换行等。不是最优雅的代码,但它完成了工作。
这个解决方案将有助于在运行时计算高度和宽度。
let messageText = "Your Text String"
let size = CGSize.init(width: 250, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let estimateFrame = NSString(string: messageText).boundingRect(with: size, options: options, attributes: [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue", size: 17)!], context: nil)
在这里,你可以计算你的字符串的估计高度,并将其传递给UILabel框架。
estimateFrame.Width
estimateFrame.Height
斯威夫特5:
如果你有UILabel和某种方式boundingRect不为你工作(我面临这个问题。它总是返回1行高)有一个扩展,可以轻松计算标签大小。
extension UILabel {
func getSize(constrainedWidth: CGFloat) -> CGSize {
return systemLayoutSizeFitting(CGSize(width: constrainedWidth, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
}
}
你可以这样使用它:
let label = UILabel()
label.text = "My text\nIs\nAwesome"
let labelSize = label.getSize(constrainedWidth:200.0)
对我有用
我无法让@KaanDedeoglu的解决方案在Swift 5中为多行标签和文本视图工作-无论出于什么原因-所以我只是写了一个“手工”解决方案,保持与@KaanDedeoglu的答案中看到的相同的函数签名。在我的程序中就像有魅力一样。
宽度
extension String {
func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
var wordBoxes = [CGSize]()
var calculatedHeight = CGFloat.zero
var calculatedWidth = CGFloat.zero
for word in self.wordsWithWordSeparators() {
let box = word.boundingRect(with: CGSize.zero, attributes: [.font: font], context: nil)
let boxSize = CGSize(width: box.width, height: box.height)
wordBoxes.append(boxSize)
calculatedHeight += boxSize.height
calculatedWidth = calculatedWidth < boxSize.width ? boxSize.width : calculatedWidth
}
while calculatedHeight > height && wordBoxes.count > 1 {
var bestLineToRelocate = wordBoxes.count - 1
for i in 1..<wordBoxes.count {
let bestPotentialWidth = wordBoxes[bestLineToRelocate - 1].width + wordBoxes[bestLineToRelocate].width
let thisPotentialWidth = wordBoxes[i - 1].width + wordBoxes[i].width
if bestPotentialWidth > thisPotentialWidth {
bestLineToRelocate = i
}
}
calculatedHeight -= wordBoxes[bestLineToRelocate].height
wordBoxes[bestLineToRelocate - 1].width += wordBoxes[bestLineToRelocate].width
wordBoxes.remove(at: bestLineToRelocate)
calculatedWidth = max(wordBoxes[bestLineToRelocate - 1].width, calculatedWidth)
}
return ceil(calculatedWidth)
}
}
高度
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
var wordBoxes = [CGSize]()
var calculatedHeight = CGFloat.zero
var currentLine = 0
for word in self.wordsWithWordSeparators() {
let box = word.boundingRect(with: CGSize.zero, attributes: [.font: font], context: nil)
let boxSize = CGSize(width: box.width, height: box.height)
if wordBoxes.isEmpty == true {
wordBoxes.append(boxSize)
}
else if wordBoxes[currentLine].width + boxSize.width > width {
wordBoxes.append(boxSize)
currentLine += 1
}
else {
wordBoxes[currentLine].width += boxSize.width
wordBoxes[currentLine].height = max(wordBoxes[currentLine].height, boxSize.height)
}
}
for wordBox in wordBoxes {
calculatedHeight += wordBox.height
}
return calculatedHeight
}
}
使用的辅助方法
extension String {
// Should work with any language supported by Apple
func wordsWithWordSeparators () -> [String] {
let range = self.startIndex..<self.endIndex
var words = [String]()
self.enumerateSubstrings(in: range, options: .byWords) { (substr, substrRange, enclosingRange, stop) in
let wordWithWordSeparators = String(self[enclosingRange])
words.append(wordWithWordSeparators)
}
return words
}
}
注意:这些高度和宽度计算假设给定的标签或文本视图在执行换行时不会拆分或连字符。如果这不是你的情况,你应该只用文字来代替字符。此外,如果您处于运行时敏感的环境中,可能需要考虑限制这些函数调用或缓存结果,因为根据字符串包含的单词数量,它们的开销可能会有点大。
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)
}
func width(withConstraintedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return ceil(boundingBox.width)
}
}