I want to create something like this:
The background/highlight effect is a rounded rectangle with .regularMaterial
and .stroke(.black)
.
I have tried using AttributedString
and setting the .backgroundColor
but this doesn’t allow me to use materials, nor can I round the edges.
I tried calculating this "precise" / "clingy" border using information about the font, but Text
sometimes automatically hyphenates long words unpredictably and thus my calculations get messed up. I have simply disabled hyphenation for now but I’m wondering if someone has figured out a way to do this.
I thought of rendering each line separately but then again Text
doesn’t tell me where in the text the line breaks will be, so I didn’t try this method.
2
Answers
A sample of code that uses these concepts is shown here:
}
Screenshot
One way to approach this is to use a
Canvas
to show the text:Canvas
receives aGraphicsContext
, which can be used to measure the width of some text.union
It’s a bit clumsy, especially in the way the text is broken up and then re-assembled, but it gets close to the result you were after. However, the one thing I couldn’t get to work was the
Material
background. If you try to useGraphicsContext.fill
to fill a path using aMaterial
style, it just fills with black (like redacted text). So the version below just uses a semi-transparent background:A
Canvas
is greedy and consumes all the space available. If you want to restrict the height of the rendered text to the minimum necessary then you can use the technique of a hidden footprint, then show theCanvas
in an overlay:It turns out that this reserves a little more height than necessary, because
Text
adds a little spacing between the wrapped lines. The functionrenderText
does not include any spacing between lines, but you could add it if you wanted to. See also How to get the default LineSpacing of a font in SwiftUI?.