10.6 C
New York
Sunday, April 6, 2025

ios – Tips on how to precisely calculate top of advanced NSAttributedString with customized fonts, paragraph spacing, and picture attachments?


I am producing a fancy NSAttributedString utilizing a number of kinds, fonts, and even inline picture attachments (icons), and I am attempting to calculate the precise top it will take when displayed in a UILabel or related view with a set width.

Here is my attributed string constructing perform:

func getStringBasic(obj: TemplateModel) -> NSAttributedString {
    let attributedString = NSMutableAttributedString()
    
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineSpacing = 6
    
    let titleAttributes: [NSAttributedString.Key: Any] = [
        .font: ThemeFont.splineBold(size: 16),
        .foregroundColor: ThemeColor.textBlack,
        .paragraphStyle: paragraphStyle
    ]
    
    let otherAttributes: [NSAttributedString.Key: Any] = [
        .font: ThemeFont.splineRegular(size: 10),
        .foregroundColor: ThemeColor.textBlack,
        .paragraphStyle: paragraphStyle
    ]
    
    let paragraphStyle2 = NSMutableParagraphStyle()
    paragraphStyle2.lineSpacing = -6
    let spaceAttributes: [NSAttributedString.Key: Any] = [
        .font: ThemeFont.splineRegular(size: 10),
        .foregroundColor: ThemeColor.textBlack,
        .paragraphStyle: paragraphStyle2
    ]

    // Customized logic to assemble string components...
    // Instance:
    let titleText = NSAttributedString(string: "(obj.cityStateCountry ?? "")n", attributes: titleAttributes)
    attributedString.append(titleText)

    let addressText = NSAttributedString(string: "(shortAddress)n(coordinates)", attributes: otherAttributes)
    attributedString.append(addressText)

    // Date, identify, contact, and so on., with non-obligatory picture attachments
    if obj.tempSetting.arrSelectedFields.incorporates(.ContactNumber) {
        let icon = createImageAttachment(imageName: "iconCall", measurement: 14, yOffset: -4)
        attributedString.append(icon)
    }

    // Extra strings appended equally...

    return attributedString
}

Then I attempt to calculate the peak like this:

func getAttributedStringHeight(for attributedString: NSAttributedString, width: CGFloat) -> CGFloat {
    let textStorage = NSTextStorage(attributedString: attributedString)
    let layoutManager = NSLayoutManager()
    let textContainer = NSTextContainer(measurement: CGSize(width: width, top: .greatestFiniteMagnitude))
    textContainer.lineFragmentPadding = 0
    textContainer.maximumNumberOfLines = 0
    textContainer.lineBreakMode = .byWordWrapping

    layoutManager.addTextContainer(textContainer)
    textStorage.addLayoutManager(layoutManager)

    _ = layoutManager.glyphRange(for: textContainer)
    let top = layoutManager.usedRect(for: textContainer).top

    return ceil(top)
}

However the returned top just isn’t completely correct. Typically the textual content will get minimize off or there’s additional area. I believe this is because of:

Customized fonts (ThemeFont.splineRegular/Daring)

Paragraph spacing (6 / -6 values)

Picture attachments (NSTextAttachment with icon photographs)

Presumably completely different baselines?

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles