gonzalezreal / swift-markdown-ui

Display and customize Markdown text in SwiftUI
MIT License
2.25k stars 268 forks source link

[Bug] `lineLimit` truncation does not work as expected when markdown content ends with a link #115

Closed maciesielka closed 1 year ago

maciesielka commented 1 year ago

Build Information

Markdown UI Version: Latest (1.1.1 at time of writing) Xcode Version: 13.4.1 iOS Simulator: iPhone 13 Pro, iOS 15.5 (reproduces on a physical iPhone 11 Pro Max -- iOS 15.5 as well)

Issue Description

There seems to be an issue with how the lineLimit modifier affects Markdown when the content ends in a link.

In the example below, the intention is to truncate the full body of the markdown at 4 lines.

import MarkdownUI
import SwiftUI

struct ContentView: View {
    var text: String {
        """
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
        Aenean a dolor at enim varius faucibus. Vivamus eget \
        ultrices lorem, non scelerisque enim.

        [https://www.google.com](https://www.google.com)
        """
    }

    var body: some View {
        Markdown(text)
            .lineLimit(4)
            .truncationMode(.tail)
            .padding(16)
    }
}
This renders as below, with the non-truncated version that just removes the lineLimit modifier shown at right: Truncated Non-truncated
Screen Shot 2022-07-27 at 12 24 48 PM Screen Shot 2022-07-27 at 12 23 09 PM

You can see that the link that is last in the markdown text is shown in both versions, despite being the last line of content overall and not the last line you'd expect to see of truncated content.

The strangeness worsens applying line-spacing, which doesn't seem to impact the spacing preceding the link:

    var body: some View {
        Markdown(text)
            .lineSpacing(12) // <-- addition here
            .lineLimit(4)
            .truncationMode(.tail)
            .padding(16)
    }

Screenshot here:

Screen Shot 2022-07-27 at 12 30 42 PM

The last thing to mention is that this behavior doesn't seem to happen when using the built-in SwiftUI.Text markdown support, i.e. Text(.init(text)) in the example above.

gonzalezreal commented 1 year ago

Hi @maciesielka,

This is an interesting issue. MarkdownUI renders the markdown into an NSAttributedString and then uses a UITextView/NSTextView to display it in a SwiftUI view hierarchy. MarkdownUI uses the truncation mode, line limit, and line spacing from the SwiftUI environment in the following ways:

The markdown example that you're providing has two paragraphs, and I suspect that the text container is applying truncation mode and line limit per paragraph, which can be the expected behaviour. If you squash the link together with the text in the same paragraph you should see what you expect.

SwiftUI Text markdown support is limited to inline markdown (emphasis, strong, links, etc.) and does not cover blocks (headings, paragraphs, lists, quotes, code blocks, thematic breaks).

Let me know if this clarifies the behaviour you're seeing.

Thanks! Guille

maciesielka commented 1 year ago

Ahh okay. Not ideal, but that makes sense at a high level. Maybe I'll use a workaround to remove the interior spacing in the text while I'm attempting truncation.

Appreciate the response!

totoroyyb commented 1 year ago

Hi,

I also tried to use the lineLimit to limit the number of lines the MarkdownUI displays. However, I've encountered the same issue as discussed above. After reading over the discussion above, I agree it's not necessarily a bug.

However, I argue this is a wrong behavior based on the lineLimit semantic in SwiftUI view. It probably should correctly limit the entire content instead of applying it to each paragraph, just like how lineLimit behaves on Text.

I wonder if there is any plan to correct the behavior.

gonzalezreal commented 1 year ago

Hi @totoroyyb,

As I mentioned in #188 (a related issue), it is very challenging to implement the truncation behavior that the Text view has. The reason is that a Markdown view is composed of multiple Text and Image views using different layouts.

Here is a list of workarounds:

I could implement a markdownWordLimit(_:) modifier that limits the Markdown contents to the specified number of words. Would that solve your use case?