这是我的 AttributedLabel 类:
import UIKit
class AttributedLabel: UILabel {
var cornerRadius: CGFloat = 3 {
didSet {
layer.cornerRadius = cornerRadius
}
}
var borderColor: UIColor = .clear {
didSet {
layer.borderColor = borderColor.cgColor
layer.borderWidth = 1
}
}
var masksToBounds: Bool = false {
didSet {
layer.masksToBounds = masksToBounds
}
}
var borderWidth: CGFloat = 1 {
didSet {
layer.borderWidth = borderWidth
}
}
private var strikeThrough = false
private var paddingTop: CGFloat = 0
private var paddingBottom: CGFloat = 0
private var paddingLeft: CGFloat = 0
private var paddingRight: CGFloat = 0
override var text: String? {
didSet {
if strikeThrough {
attributedText = NSAttributedString(string: text ?? "", strikeThrough: true)
}
}
}
// MARK: - Initialization
convenience init(
paddingTop: CGFloat = 0,
paddingBottom: CGFloat = 0,
paddingLeft: CGFloat = 0,
paddingRight: CGFloat = 0,
strikeThrough: Bool = false
) {
self.init()
self.paddingTop = paddingTop
self.paddingBottom = paddingBottom
self.paddingLeft = paddingLeft
self.paddingRight = paddingRight
self.strikeThrough = strikeThrough
backgroundColor = .yellow
}
// MARK: - Overriden
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets(top: paddingTop, left: paddingLeft, bottom: paddingBottom, right: paddingRight)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
var contentSize = super.intrinsicContentSize
contentSize.height += paddingTop
contentSize.height += paddingBottom
contentSize.width += paddingLeft
contentSize.width += paddingRight
return contentSize
}
}
这就是结果:
第一个标签被截断。为什么?它的定义是这样的:
private let titleLabel: UILabel = {
let label = AttributedLabel(paddingLeft: 20, paddingRight: 20)
label.textColor = .mineShaft
label.font = UIFont.poppinsMedium.withSize(18)
label.textAlignment = .left
label.numberOfLines = 0
return label
}()
并已分配文本:
titleLabel.text = "Czy jesteś za wdrożeniem aplikacji PLZ?"
我不知道这是否重要,但 titleLabel 已添加到 UIStackView 中:
contentStackView.addArrangedSubview(titleLabel)
使用您的代码:
override var intrinsicContentSize: CGSize {
var contentSize = super.intrinsicContentSize
// etc...
return contentSize
}
文本不会换行,因为标签没有固有宽度。因此,如果您将整个段落用作
.text
contentSize
,最终可能会变成类似 (1500, 20)
您想要使用内部
CGSize
属性,计算 layoutSubviews()
中文本的高度,然后调用 invalidateIntrinsicContentSize()
。
这是一个简单的例子:
助手扩展:
extension NSAttributedString {
func boundingRect(for size: CGSize) -> CGRect {
let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
return boundingRect(with: size, options: options, context: nil)
}
}
您的课程,经过一些修改:
class AttributedLabel: UILabel {
var cornerRadius: CGFloat = 3 {
didSet {
layer.cornerRadius = cornerRadius
}
}
var borderColor: UIColor = .clear {
didSet {
layer.borderColor = borderColor.cgColor
layer.borderWidth = 1
}
}
var masksToBounds: Bool = false {
didSet {
layer.masksToBounds = masksToBounds
}
}
var borderWidth: CGFloat = 1 {
didSet {
layer.borderWidth = borderWidth
}
}
// will be set in layoutSubviews()
private var mySize: CGSize = .zero
private var strikeThrough = false
private var paddingTop: CGFloat = 0
private var paddingBottom: CGFloat = 0
private var paddingLeft: CGFloat = 0
private var paddingRight: CGFloat = 0
override var text: String? {
didSet {
let attributedString = NSAttributedString(string: text ?? "", attributes: [
.font: self.font ?? UIFont.systemFont(ofSize: 18, weight: .regular)
])
self.attributedText = attributedString
setNeedsLayout()
}
}
// MARK: - Initialization
convenience init(
paddingTop: CGFloat = 0,
paddingBottom: CGFloat = 0,
paddingLeft: CGFloat = 0,
paddingRight: CGFloat = 0,
strikeThrough: Bool = false
) {
self.init()
self.paddingTop = paddingTop
self.paddingBottom = paddingBottom
self.paddingLeft = paddingLeft
self.paddingRight = paddingRight
self.strikeThrough = strikeThrough
backgroundColor = .yellow
}
// MARK: - Overriden
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets(top: paddingTop, left: paddingLeft, bottom: paddingBottom, right: paddingRight)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
// super.intrinsicContentSize will NOT wrap the text
//var contentSize = super.intrinsicContentSize
var contentSize = mySize
contentSize.height += paddingTop
contentSize.height += paddingBottom
contentSize.width += paddingLeft
contentSize.width += paddingRight
return contentSize
}
override func layoutSubviews() {
super.layoutSubviews()
// this will be called more than once...
// during development, log the calls to make sure we're not in an infinite loop
print(#function)
let attributedString = self.attributedText ?? NSAttributedString(string: "")
mySize = attributedString.boundingRect(for: .init(width: bounds.width - (paddingLeft + paddingRight), height: .greatestFiniteMagnitude)).size
invalidateIntrinsicContentSize()
}
}
视图控制器示例:
class LabelTestVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let st = UIStackView()
st.axis = .vertical
st.spacing = 12
st.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(st)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
st.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
st.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
st.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
let samples: [String] = [
"Czy jesteś za wdrożeniem aplikacji PLZ?",
"single line",
"This will be long enough that we're sure it will need to wrap.",
"This example has\nembedded\nnewLine characters\nso we get 4 lines.",
]
for str in samples {
let label = AttributedLabel(paddingLeft: 20, paddingRight: 20)
label.textColor = .red // .mineShaft
label.font = .systemFont(ofSize: 18.0, weight: .regular) //UIFont.poppinsMedium.withSize(18)
label.textAlignment = .left
label.numberOfLines = 0
label.text = str
st.addArrangedSubview(label)
// so we can see the framing
label.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
}
// so we can see the framing
st.backgroundColor = .systemBlue
}
}
结果(iPhone 15 Pro):
并且设备旋转: