Is there no way to determine actual/correct NSString boundingRect height for any/random UIFont?

331 views Asked by At

I have tried everything I found on StackOverflow but nothing worked really. I need to calculate boundingRect's height for any/random UIFont available.

The code is simple (I modified some of the code to be more readable):

let ps = NSMutableParagraphStyle()
ps.lineBreakMode = NSLineBreakMode.byWordWrapping
ps.alignment = NSTextAlignment.center
ps.lineSpacing = 0
ps.paragraphSpacingBefore = 0
ps.paragraphSpacing = 0

let myRandomFont = geMyRandomFont(name: randomFontName, size: myFontSize)
let attrParams = [
    NSAttributedString.Key.font: myRandomFont,
    NSAttributedString.Key.paragraphStyle: ps
    ] as [NSAttributedString.Key : Any]

let ns = NSString(string: text)
let toUseForDrawingBounds = ns.boundingRect(
    with: CGSize(width: boundsSize.width, height: .greatestFiniteMagnitude),
    options: [.usesLineFragmentOrigin, .usesFontLeading],
    attributes: attrParams,
    context: nil
)

In rare cases (for some UIFonts) it works ok, but for most of the other fonts toUseForDrawingBounds.height is almost always 1 blank line too big. (my input string text is always trimmed with whitespacesAndNewlines)

EDIT 2: After some testing I noticed this was not the case. With every UIFont (different combinations of font sizes & text) it can happen the returned height is 1 blank line too big. When I calculated number of lines (toUseForDrawingBounds.height / font.lineHeight) I clearly saw that sometimes returned number of lines was 1 too many.

I have tried tricks (StackOverflow) with (sizeToFit, etc.) UITextViews, UILabels, etc. Nothing really worked. Any ideas?

EDIT: To give a clear example, look at the attached image. Yellow rectangle approx. represents returned toUseForDrawingBounds bounds. I need proper bounds height so I can draw text vertically centered.

enter image description here

2

There are 2 answers

2
PGDev On

You can simply use size(withAttributes:) on String to get its height and width, i.e.

let text = "This is the sample text."
let textSize = text.size(withAttributes: [.font: myRandomFont, .paragraphStyle: ps])
print(textSize)

The above code is just an example of how you can proceed. Add the attributes as per your requirement.

0
sabiland On

Though I do not understand downvotes (because it is not trivial solution for any random font-name and random text combination), I have somehow managed to approx. solve my problem.

Just to summarize if anyone should have the same problems. I wanted to get an optimal font-size for randomly chosen font-name and randomly chosen input text to be drawn inside specified CGRect (so, wordWrapped).

I did this like this:

  1. For random font-name I took character "W" and with String.size(withAttributes:) and NSLineBreakMode.byWordWrapping I calculated maximum possible (binary search) fontSize to fit in CGRect
  2. Then I checked if fontSize > CGRect.height (yes it can happen!) and reduced fontSize to CGRect.height (actually this 2 step solved almost all of my issues)
  3. Then I just sqrt(pow(fontSize, 2) / numOfChars)) to get approx. optimal font-size to draw a text in a CGRect