johnxnguyen / Down

Blazing fast Markdown / CommonMark rendering in Swift, built upon cmark.
Other
2.24k stars 319 forks source link

Support for the new `AttributedString` #262

Open liang2kl opened 3 years ago

liang2kl commented 3 years ago

In WWDC 21 a new AttributedString type was introduced, useful for rendering attributed text in SwiftUI.

It seems that converting the NSAttributedString instance generated by the module to AttributedString using init<S>(_ nsStr: NSAttributedString, including scope: S.Type) throws where S : AttributeScope does not preserve any renderable attribute, as shown in the figure, where the top view is the attributed text:

IMG_7042

The code to generate the attributed string is:

let markdownString = try! Down(markdownString: text).toAttributedString()
let attributedString = try! AttributedString(markdownString, including: \.foundation)

To display:

Text(attributedString)

Maybe some fixes or directly generating the attributed string could help.

johnxnguyen commented 3 years ago

Hi @liang2kl , I haven't been able to look at this yet. Is this no longer an issue?

liang2kl commented 3 years ago

Hi @liang2kl , I haven't been able to look at this yet. Is this no longer an issue?

Sorry, seems I closed this by mistake.

I have experimented with this for a while, and the reason might be SwiftUI not recognizing the attributes created with UIKit (the AttributedString instance do have the original attributes, though).

So I guess we need a styler for AttributedString?

johnxnguyen commented 3 years ago

I'm afraid I haven't yet had a look at the new AttributedString api, so I welcome any input that you have.

liang2kl commented 3 years ago

I have a solution after some exploration.

The problem is that, SwiftUI won't recognize any attribute from UIKit. Instead, it use different types, with limited attributing options.

So (it seems) the only solution is to convert the UIKit attributes to the SwiftUI ones, like:

var attrStr: AttributedString = ...

for run in attrStr.runs {
    if let font = run.uiKit.font {
        attrStr[run.range].swiftUI.font = Font(font as CTFont)
    }
}

This method can transform the correspond attribute to any currently supported attribute in SwiftUI, and I works. But as SwiftUI's attributing support is very poor, not every original attribute can be transformed and displayed. So I am afraid that there's no perfect solution to displaying Markdown in SwiftUI currently.

johnxnguyen commented 3 years ago

But as SwiftUI's attributing support is very poor, not every original attribute can be transformed and displayed.

@liang2kl I would wait until AttributedString matures enough to be able to support the standard TextKit attributes, otherwise, as you suggest, the experience will be quite poor. In any case, I'll return to this at some point to see how we can take advantage of new TextKit apis.