// // LineSpaceLabel.swift // Crush // // Created by Leon on 2025/7/18. // import UIKit class LineSpaceLabel: UILabel { // MARK: - Properties var paragraphSpace: CGFloat = 0 private var typography: EPTypography? private var typographyText: String? // MARK: - Initialization override init(frame: CGRect) { super.init(frame: frame) numberOfLines = 0 textColor = .text guard let text = typographyText, !text.isEmpty else { return } self.text = text } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Public Methods (Way 1) func setupLineSpace(_ space: CGFloat) { guard let text = text, !text.isEmpty else { assertionFailure("self.text should not be empty.") return } let attributedString: NSMutableAttributedString if let existingAttributedText = attributedText { attributedString = NSMutableAttributedString(attributedString: existingAttributedText) } else { attributedString = NSMutableAttributedString(string: text) } let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = space attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: text.count)) self.text = nil attributedText = attributedString lineBreakMode = .byTruncatingTail } func setupText(_ text: String?, lineSpace: CGFloat) { let safeText = text ?? "" let attributedString = NSMutableAttributedString(string: safeText) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineSpace attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: safeText.count)) attributedText = attributedString lineBreakMode = .byTruncatingTail } func setupHighlightText(_ highlight: String?, color: UIColor) { guard let attributedText = attributedText, let highlight = highlight, !highlight.isEmpty else { assertionFailure("attributedText and highlight must be valid") return } let attributedString = NSMutableAttributedString(attributedString: attributedText) if let range = attributedString.string.range(of: highlight) { let nsRange = NSRange(range, in: attributedString.string) attributedString.addAttribute(.foregroundColor, value: color, range: nsRange) } self.attributedText = attributedString } // MARK: - Public Methods (Way 2) func config(_ typography: EPTypography) { self.typography = typography self.text = text } // MARK: - Overrides override var text: String? { get { super.text } set { let safeText = newValue ?? "" if let typography = typography { typographyText = safeText let attributes: [NSAttributedString.Key: Any] = [ .font: typography.font!, .foregroundColor: textColor ?? .black, ] let attributedString = NSMutableAttributedString(string: safeText, attributes: attributes) let calLineHeight = typography.lineHeight * 0.5 // Temporary conversion, may need testing let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = calLineHeight paragraphStyle.alignment = textAlignment if paragraphSpace > 0 { paragraphStyle.paragraphSpacing = paragraphSpace } attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: safeText.count)) self.attributedText = attributedString self.lineBreakMode = .byTruncatingTail layoutIfNeeded() } else { super.text = safeText } } } override var textColor: UIColor! { get { super.textColor } set { super.textColor = newValue if let typographyText = typographyText, !typographyText.isEmpty { text = typographyText } } } }