我无法让@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
}
}
注意:这些高度和宽度计算假设给定的标签或文本视图在执行换行时不会拆分或连字符。如果你不是这种情况,你应该只需要用单词代替字符。此外,如果您处于运行时敏感环境中,可能需要考虑限制这些函数调用或缓存结果,因为根据字符串包含的单词数量,它们可能会有点昂贵。