Damus
Frederik Handberg profile picture
Frederik Handberg
@frederikhandberg
Using `Text()` from #SwiftUI has turned out to be a bit of a problem - at least in the visual canvas.

In SwiftUI, the easiest way to zoom in on a view is to use `.scaleEffect()`. This is exactly what I did in the canvas, so when you zoom in on a text object, I would increase the text size by simply using a `.scaleEffect()` modifier. However, this caused an annoying problem where the text would become pixelated, so this approach was just not viable. I had to find a different way...

I tried another approach where I would dynamically scale the font size (multiplying the base font by the zoom scale). This did indeed solve the problem for normal text objects, so the text no longer became pixelated. But this approach introduced it’s own problem for note objects which also use `Text()` from SwiftUI. It caused layout jumping.

If I understand it correctly, the rendering engine called `CoreText` does not scale fonts linearly. For example, a 14pt font scaled to 200% doesn't always take up exactly twice the space of a 7pt font. I noticed that small adjustments to letter-spacing would happen at every fractional font size.

This meant that as the user zooms, a word that barely fit on line one suddenly becomes 0.1 pixels too wide and wraps to line two. This would cause the entire text block to "jump" vertically as lines snap back and forth between different wrap points. It felt broken and looked bad.


The solution ended up being to use AppKit. Unlike SwiftUI’s `Text()`, the `NSTextView` from #AppKit allows us to manipulate the text to a much greater degree.

So this did indeed fix the problem for note objects. BUT... It also introduced a problem since I use `ImageRenderer` to take a snapshot of the canvas, which is then used to display a preview of the canvas in search results.

The thing is that `ImageRenderer` **ONLY** works with SwiftUI. It is simply incapable of capturing an AppKit view like `NSViewRepresentable`. It seems like my only choice is to render an `NSTextView` in the canvas, and then render SwiftUI `Text()` when the app needs to take a snapshot with the `ImageRenderer` API. #dev
1❤️1
Frederik Handberg · 1w
After having spent some time testing this new solution, it seems to work great. I am still missing the updated scaling code in a few block types like tables and list items.