gonzalezreal / swift-markdown-ui

Display and customize Markdown text in SwiftUI
MIT License
2.26k stars 267 forks source link

Style modifiers, deep links #47

Closed antonjazz closed 2 years ago

antonjazz commented 3 years ago

I'm very much looking forward to using MarkdownUI. Thank you!! It's so easy to use as far as standard rendering goes.

This is a question regarding customization. I have a few issues, and I know that you're very busy, but if you're able to address any of them, especially the first, that would be fantastic!

  1. I've looked all through MarkdownUIDemo, and I'm afraid I still don't understand the intended way to use the MarkdownStyle function variables such as headingAttributes(). I don't suppose you can point me to any example? I'm using MarkdownUI in a SwiftUI project and need to be able to customize the colors and spacings of the various heading levels.
  2. I want to use MarkdownUI for in-app documentation, where the goal is to be able to display multiple pages of markdown and have them link to each other via in-text links. I was thinking I'd use standard markdown links and set up my app to handle Deep Link URLs, such that onOpenURL(perform:) can respond to a URL link like my-url-scheme://lookup/documentation-term by displaying the page for documentation-term. Does that sound like a reasonable approach?
  3. Much less important, but have you given any thought to use of SF Symbols symbols? I realize that that would go beyond the markdown spec, but they're so useful! :)

Thank you again for the great package!!

gonzalezreal commented 3 years ago

Hi @antonjazz,

If you want to have control over the spacing and the colours of the headings, you will need to create a type that implements MarkdownStyle. Notice that MarkdownStyle provides default implementations for all the different markdown elements, so you will only need to implement headingAttributes(_:level:paragraphState:). Here is an example showing how to provide different colours and spacings for the headings:

struct CustomHeadingStyle: MarkdownStyle {
    let font: Font = .system(.body)
    let foregroundColor: Color = .label
    let codeFontName: String? = nil
    let codeFontSizeMultiple: CGFloat = 0.94
    let headingFontSizeMultiples: [CGFloat] = [2, 1.5, 1.17, 1, 0.83, 0.67]

    let headingForegroundColors: [Color] = [
        .label, .secondaryLabel, .tertiaryLabel, .quaternaryLabel
    ]
    let headingSpacingMultiples: [CGFloat] = [1.5, 0.67]

    public func headingAttributes(
        _ attributes: inout [NSAttributedString.Key : Any],
        level: Int,
        paragraphState: ParagraphState
    ) {
        attributes[.font] = makeHeadingFont(level)

        let color = headingForegroundColors[min(level, headingForegroundColors.count) - 1]
        attributes[.foregroundColor] = color

        let spacingMultiple = headingSpacingMultiples[min(level, headingSpacingMultiples.count) - 1]
        attributes[.paragraphStyle] = makeParagraphStyle(
            for: paragraphState,
            spacingMultiple: spacingMultiple
        )
    }
}

You can apply the custom style like this:

struct ContentView: View {
    var body: some View {
        Markdown(
            #"""
            # After the Big Bang
            A brief summary of time
            ## Life on earth
            10 billion years
            ### You reading this
            13.7 billion years
            """#
        )
        .markdownStyle(CustomHeadingStyle())
    }
}

And the result should be as follows:

Simulator Screen Shot - iPhone 8 - 2021-04-30 at 11 49 34

Regarding your question about deep links, the approach you're suggesting should work, as MarkdownUI delegates URL handling to the system.

About your last question, what should be the use case for SF Symbols?

antonjazz commented 3 years ago

Thank you, @gonzalezreal - that's exactly what I needed! :) Have you considered including one example like this in the demo? It would be so useful to see an example using one of the protocol functions such as headingAttributes(_:level:paragraphState:) or linkAttributes(_:url:title:) customized like this.

As for SF Symbols, I was thinking of the string interpolation uses that come in handy in Text views, such as "\(Image(systemName: "lightbulb.fill")) Idea" or "\(Image(systemName: "checkmark.circle")) Done". I find myself making more and more use of these symbols for clarity in text. I don't have a suggestion for the markdown syntax, but perhaps there is a precedent to follow, for extensions to markdown for such things?

Thanks again!

Addendum:

I tried it out and it works great. There are just a couple things I'm still looking for a way to do…

  1. Have the spacing between successive paragraphs be smaller than the spacing between a paragraph and a header that follows it.
  2. Create a bit of extra space between list items, for clarity (like github does subtly 😃 )
  3. Have headings of different levels be different font weights.

If any of those are possible, I'd love to know. Thank you!