Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a glyph boundingRect in draw#rect in UILabel

Using Swift, I want to get the boundingRect of a glyph, in draw#rect in a UILabel.

The UILabel already has a size (say 300x300 in the example) and qualities such as the text being centered.

class RNDLabel: UILabel {

    override func draw(_ rect: CGRect) {

        let manager = NSLayoutManager()

        let store = NSTextStorage(attributedString: NSAttributedString(
            string: text!,
            attributes: [NSAttributedString.Key.font: font]))
        store.addLayoutManager(manager)

        let textContainer = NSTextContainer(size: rect.size)
        // note, intrinsicContentSize is identical there, no difference
        manager.addTextContainer(textContainer)

        let glyphRange = manager.glyphRange(
           forCharacterRange: NSRange(location: 0, length: 1),
           actualCharacterRange: nil)
        let glyphRect = manager.boundingRect(
           forGlyphRange: glyphRange, in: textContainer)

        print("glyphRect \(glyphRect)")         
        ...context?.addRect(glyphRect), context?.drawPath(using: .stroke)

        super.draw(rect)
    }

enter image description here

The green square is not correct - it should be more like these red squares!

enter image description here

There seems to be a number of problems:

  • surely the layoutManager I make, should get the qualities of the UILabel, eg "centered text"? (I believe you can't actually directly access the layoutManager of a UILabel, though?)

  • should we be using something like CTLineGetOffsetForStringIndex? Is that even possible in draw#rect

  • notice as well as not having the correct offset, the green box seems the wrong height anyway. (It looks more like a plain old intrinsicContentSize rather than a glyph bounding box.)

How to?

For the record, my overall aim is to move the glyph around, based on the actual glyph box (which of course is different for "y", "X", etc). But in general there are many useful reasons to know the box of a glyph.

like image 732
Fattie Avatar asked Nov 07 '22 15:11

Fattie


1 Answers

Your original question is sort of an unquestion, because it says two completely opposite things.

  • On the one hand, you say UILabel.

  • On the other hand, you use terms like NSLayoutManager and NSTextStorage and NSTextContainer — the TextKit stack.

Those are opposites because UILabel is not drawn with the TextKit stack. So what you're asking to do is like trying to lift a piece of paper with a magnet; magnets do lift things, but what they lift are magnetic materials, and paper is not one of those. To be sure, UILabel must draw in some deterministic way, but what that way is, is completely unknown and opaque and has nothing to do with TextKit.

On the other hand, UITextView, or a view that your draw yourself using TextKit, does use TextKit, and now all the TextKit tools apply. So what you're asking to do would be a lot more straightforward if this were a UITextView (or a view that you draw yourself using TextKit).

This need not change anything very much about the appearance of the interface. A UITextView can be made to act a lot like a UILabel (make it noneditable and nonscrollable) but with the important difference that the whole text kit stack is directly exposed. So that's where I'd start. I'm one of those people who prefers to use the framework rather than to fight it.

like image 73
matt Avatar answered Nov 13 '22 21:11

matt