带填充的自定义 UILabel 的内在内容大小错误

问题描述 投票:0回答:1

这是我的 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)
ios swift uilabel
1个回答
0
投票

使用您的代码:

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):

并且设备旋转:

© www.soinside.com 2019 - 2024. All rights reserved.