swiftuiswiftui-layout

How to prevent text truncation when inside a ViewThatFits and with maxWidth set?


I'm curious, has anyone run into this before? I have a situation where long Text that is inside a width-limited view is being unnecessarily truncated so that it fits vertically within the parent. Is there a way to turn off that truncation?

Here's some code to demonstrate the problem:

struct TextTruncator: View {
    @State private var longText = false
    var body: some View {
        Toggle("Long Text", isOn: $longText).padding()
        essay
    }
    
    var essay: some View {
        VStack {
            paragraph
            if longText {
                paragraph
                paragraph
                paragraph
            }
        }
        .frame(maxWidth: 200)
    }
    
    var paragraph: some View {
        Text("+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
    }
}

How can we disable the truncation that happens when we're using Long Text? Of course, one naturally would think of putting the Text inside a ScrollView, like so:

    var body: some View {
        Toggle("Long Text", isOn: $longText).padding()
        ScrollView {
            essay
        }
    }

But that doesn't suit my needs, because in my actual context, my Text equivalent is followed by some other controls that I want to immediately follow the Text. If I use the ScrollView as above, the scrollView will expand to fill the screen, even if the Text is short, and my controls will be too far from the Text.

So I tried to solve this by using a ViewThatFits to have SwiftUI automatically switch from the vanilla Text to a version that is embedded in a ScrollView. So we end up with something like this:

    var body: some View {
        Toggle("Long Text", isOn: $longText).padding()
        ViewThatFits {
            essay
            ScrollView {
                essay
            }
        }
        Button("My Controls") {}
        Spacer()
    }

And this is where the truncation really becomes a problem. It is preventing the Text section from being big enough to trigger the ViewThatFits to switch to the ScrollView version, as I would like.

Oh, and by the way, the .frame(maxWidth:) is needed because otherwise, even a single Text will be so wide that it will always trigger the switch to the ScrollView version.

So, ideally, I hope there's a way to prevent the text truncation, but if someone can suggest another way to achieve the layout I need, that would be fabulous!


Solution

  • You only want ViewThatFits to work in the vertical axis, so you should pass in: .vertical as the argument:

    ViewThatFits(in: .vertical) {
        essay
        ScrollView {
            essay
        }
    }
    

    Now you don't even need maxWidth: 200.