ios – set a uiview’s bounds to the identical measurement as NSTextLayoutFragment.layoutFragmentFrame

0
1
ios –  set a uiview’s bounds to the identical measurement as NSTextLayoutFragment.layoutFragmentFrame


I’m making an attempt to develop my very own UIView that leverages TextKit2 to take management over the textual content rendering to use customized drawing.

The issue I’m going through to this point is that the UIView is at all times greater than the precise bounds that the textual content wants.

I do know that you should utilize layoutFragmentFrame or renderingSurfaceBounds inside a subclass of NSTextLayoutFragment to get the precise bounds for the textual content – however I used to be not ready resize the view accordingly.

This screenshot illustrates the issue:

problem

My UIView implementation appears to be like like this:

import UIKit
ultimate class TextKit2UIView: UIView, NSTextLayoutManagerDelegate {
    let contentStorage = NSTextContentStorage()
    let layoutManager = NSTextLayoutManager()
    let textContainer = NSTextContainer(measurement: .zero)

    override init(body: CGRect) {
        tremendous.init(body: body)
        
        contentStorage.addTextLayoutManager(layoutManager)
        layoutManager.textContainer = textContainer
        layoutManager.delegate = self
        backgroundColor = .brown.withAlphaComponent(0.2)
        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = .byTruncatingTail
        
        contentStorage.attributedString = NSAttributedString(
            string: "Integer bibendum luctus metus, vel mattis diam consequat non. Phasellus magna augue, faucibus et ullamcorper sit amet, vehicula quis nunc. Phasellus nisl arcu, tempus id metus ac, tempus mollis nisi. Duis in ultrices lectus.",
            attributes: [.font: UIFont.systemFont(ofSize: 18)]
        )
    }

    required init?(coder: NSCoder) { nil }

    // utilizing my very own customized NSTextLayoutFragment
    func textLayoutManager(_ textLayoutManager: NSTextLayoutManager,
                           textLayoutFragmentFor location: NSTextLocation,
                           in textElement: NSTextElement) -> NSTextLayoutFragment {
        CustomLayoutFragment(textElement: textElement, vary: textElement.elementRange)
    }

    override func layoutSubviews() {
        tremendous.layoutSubviews()
        textContainer.measurement = bounds.measurement
        layoutManager.ensureLayout(for: contentStorage.documentRange)
        setNeedsDisplay()
    }

    override func draw(_ rect: CGRect) {
        guard let ctx = UIGraphicsGetCurrentContext() else { return }
        layoutManager.enumerateTextLayoutFragments(from: nil, choices: []) { fragment in
            fragment.draw(at: fragment.layoutFragmentFrame.origin, in: ctx)
            return true
        }
    }
    
}

And my NSTextLayoutFragment subclass appears to be like like this (for now it simply renders the inexperienced rectangle behind the textual content, however it is going to have way more customized drawing afterward):

class CustomLayoutFragment: NSTextLayoutFragment {
    
    override func draw(at renderingOrigin: CGPoint, in ctx: CGContext) {
        let completeRect = self.layoutFragmentFrame
        let shapeRect = Rectangle().path(in: completeRect).cgPath
        ctx.saveGState()
        ctx.addPath(shapeRect)
        ctx.setFillColor(UIColor.inexperienced.withAlphaComponent(0.2).cgColor)
        ctx.fillPath()
        ctx.restoreGState()
        tremendous.draw(at: renderingOrigin, in: ctx)
    }
    
}

May somebody please clarify learn how to obtain the specified habits? Particularly, how can I make sure that the uiView’s body matches the textual content’s body, eliminating the seen brown background that the screenshot reveals?

Please observe that I used SwiftUI to indicate the UIView on display screen and that I explicitly set the width and top of that view to some particular values to showcase the issue. The uiView (and subsequently the brown rectangle) has the width and top offered by the SwiftUI’s body modifier.

LEAVE A REPLY

Please enter your comment!
Please enter your name here