我想创建一种方法来计算多行标签的最佳宽度,以便在固定高度的水平行中附加多个标签。
使用一行文字没有问题:
let textAttributes: [String : Any] = [NSFontAttributeName: UIFont.preferredFont(forTextStyle: UIFontTextStyle.title2)]
let maximalWidth: CGFloat = text!.boundingRect(
with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: height),
options: [NSStringDrawingOptions.usesLineFragmentOrigin],
attributes: textAttributes,
context: nil).size.width
据我所知,这里没有选项,我有几行。当我们计算具有固定宽度的文本的高度时,此方法在其他方向上工作良好。但我有相反的目标。
作为一种变体,我可以根据最长的单词创建一个标签(更准确地说,基于最宽的单词,因为我们可以有几个具有相同字符数但具有不同渲染宽度的单词):
var sizeToReturn = CGSize()
let maxWordsCharacterCount = text?.maxWord.characters.count
let allLongWords: [String] = text!.wordList.filter {$0.characters.count == maxWordsCharacterCount}
var sizes: [CGFloat] = []
allLongWords.forEach {sizes.append($0.size(attributes: attributes).width)}
let minimalWidth = (sizes.max()! + constantElementsWidth)
我在这里使用了两个String扩展来创建单词列表并找到所有最长的:
extension String {
var wordList: [String] {
return Array(Set(components(separatedBy: .punctuationCharacters).joined(separator: "").components(separatedBy: " "))).filter {$0.characters.count > 0}
}
}
extension String {
var maxWord: String {
if let max = self.wordList.max(by: {$1.characters.count > $0.characters.count}) {
return max
} else {return ""}
}
}
这不是一个糟糕的选择,但如果我们的文字不能用三行编写并且最后有几个短文和一个长字,那么它看起来很难看。确定宽度的这个长词将被截断。更多的是,它看起来不太好,有3个简短的单词,如:
嗯,我有最小宽度,我有最大宽度。也许,我可以 从最大值到最小值,并在标签开始被截断时捕获。 所以我觉得可以有一个优雅的解决方案,但我被卡住了。
答案 0 :(得分:0)
万岁,我找到了可能的解决方案之一。您可以在操场上使用以下代码:
import UIKit
import PlaygroundSupport
//: Just a view to launch playground timeline preview
let hostView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
hostView.backgroundColor = .lightGray
PlaygroundPage.current.liveView = hostView
// MARK: - Extensions
extension String {
var wordList: [String] {
return Array(Set(components(separatedBy: .punctuationCharacters).joined(separator: "").components(separatedBy: " "))).filter {$0.characters.count > 0}
}
}
extension String {
var longestWord: String {
if let max = self.wordList.max(by: {$1.characters.count > $0.characters.count}) {
return max
} else {return ""}
}
}
// MARK: - Mathod
func createLabelWithOptimalLabelWidth (
requestedHeight: CGFloat,
constantElementsWidth: CGFloat,
acceptableWidthForTextOfOneLine: CGFloat, //When we don't want the text to be shrinked
text: String,
attributes: [String:Any]
) -> UILabel {
let label = UILabel(frame: .zero)
label.attributedText = NSAttributedString(string: text, attributes: attributes)
let maximalLabelWidth = label.intrinsicContentSize.width
if maximalLabelWidth < acceptableWidthForTextOfOneLine {
label.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: maximalLabelWidth, height: requestedHeight))
return label // We can go with this width
}
// Minimal width, calculated based on the longest word
let maxWordsCharacterCount = label.text!.longestWord.characters.count
let allLongWords: [String] = label.text!.wordList.filter {$0.characters.count == maxWordsCharacterCount}
var sizes: [CGFloat] = []
allLongWords.forEach {sizes.append($0.size(attributes: attributes).width)}
let minimalWidth = (sizes.max()! + constantElementsWidth)
// Height calculation
var flexibleWidth = maximalLabelWidth
var flexibleHeight = CGFloat()
var optimalWidth = CGFloat()
var optimalHeight = CGFloat()
while (flexibleHeight <= requestedHeight && flexibleWidth >= minimalWidth) {
optimalWidth = flexibleWidth
optimalHeight = flexibleHeight
flexibleWidth -= 1
flexibleHeight = label.attributedText!.boundingRect(
with: CGSize(width: flexibleWidth, height: CGFloat.greatestFiniteMagnitude),
options: [NSStringDrawingOptions.usesLineFragmentOrigin],
context: nil).size.height
print("Width: \(flexibleWidth)")
print("Height: \(flexibleHeight)")
print("_______________________")
}
print("Final Width: \(optimalWidth)")
print("Final Height: \(optimalHeight)")
label.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: optimalWidth+constantElementsWidth, height: requestedHeight))
return label
}
// MARK: - Inputs
let text: String? = "Determine the fair price"//nil//"Select the appropriate payment method"//"Finalize the order" //"Sell the car"//"Check the payment method"
let font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.callout)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping
paragraphStyle.allowsDefaultTighteningForTruncation = true
let attributes: [String:Any] = [
NSFontAttributeName: font,
NSParagraphStyleAttributeName: paragraphStyle,
NSBaselineOffsetAttributeName: 0
]
if text != nil {
let label = createLabelWithOptimalLabelWidth(requestedHeight: 70, constantElementsWidth: 0, acceptableWidthForTextOfOneLine: 120, text: text!, attributes: attributes)
label.frame.width
label.frame.height
label.backgroundColor = .white
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 3
hostView.addSubview(label)
}