How to make a scrollable multi-line Text

As you can see, I'm really struggling to create a simple multi-line Text. The closest I got so far is putting it in a List instead of a ScrollView, but then I get a line under the text.


Any help would be greatly appreciated 🙂


/// This was the most obvious approach
struct Try1 : View {
    var body: some View {
        ScrollView {
            Text(content)
        }
    }
}

/// OK, I probably need to let it wrap to multiple lines.
struct Try2 : View {
    var body: some View {
        ScrollView {
            Text(content)
            .lineLimit(nil)
        }
    }
}
/// Hmm no dice.

/// Maybe if I tell it to use the parent's size?
struct Try3 : View {
    var body: some View {
        ScrollView {
            Text(content)
                .relativeWidth(1.0)
        }
    }
}
/// Nope.


/// Maybe it needs to be a "fixed" size?
struct Try4 : View {
    var body: some View {
        ScrollView {
            Text(content)
                .lineLimit(nil)
                .fixedSize(horizontal: true, vertical: false)
        }
    }
}


/// The other way?
struct Try5 : View {
    var body: some View {
        ScrollView {
            Text(content)
                .lineLimit(nil)
                .fixedSize(horizontal: false, vertical: true)
        }
    }
}


/// Nope
struct Try6 : View {
    var body: some View {
        ScrollView {
            Text(content)
                .relativeWidth(1.0)
            }.relativeWidth(1.0)
    }
}

/// Still no text wrapping
struct Try7 : View {
    var body: some View {
        ScrollView {
            VStack {
                Text(content)
                Spacer()
            }
        }
    }
}

/// This wraps the text correctly, but then draws an unsighly horizontal line right below it :(
struct Try8 : View {
    var body: some View {
        List {
            Text(content)
                .lineLimit(nil)
        }
    }
}

Replies

Sometimes a combination of ScrollView and GeometryReader helps (but may not always):

struct Try9: View {

    var body: some View {
        return GeometryReader { geometry in
            ScrollView {
                Text(content)
                    .lineLimit(nil)
                    .frame(width: geometry.size.width)
            }
        }
    }
}


But I had also situations where the text has been shrinked (with ... at the end). Not sure why.

struct ContentView : View {
    @State var width: Length = 1
    var body: some View {
        GeometryReader {
            geometry in
        ScrollView(isScrollEnabled: true, alwaysBounceHorizontal: false, alwaysBounceVertical: true, showsHorizontalIndicator: false, showsVerticalIndicator: true) {
            VStack {
                    Text("This is a test of the emergency broadcast system. This is only a test. If this were a real emergency, then you'd be up the creek without a paddle. But it's not so you're safe for the time being.")
                        .background(Color.red)
                        .lineLimit(nil)
                        .frame(
                            minWidth: geometry.size.width,
                            idealWidth: geometry.size.width,
                            maxWidth: geometry.size.width,
                            minHeight: geometry.size.height,
                            idealHeight: geometry.size.height,
                            maxHeight: .infinity,
                            alignment: .topLeading)
                }

            }
        }
    }
}


The above appears to work, and on further experimentation, I've found both that the VStack is unnecessary, and that one can omit `idealWidth` and `idealHeight`

Why this line?


@State var width: Length = 1

Ah - likely something left over from experimentation...

Man this really seems like overkill.. I am trying to do a multi-line text item for a help screen on a watch app... This really helps.. but am totally amazed that it is necessary. Thanks for posting this!!!

I think this is in the 'release notes' as a known issues, scrollview's are a bit buggy. It should be enought to simply wrap your scrollview in a geometry reader and hard code the width as martinr92 sugested, so long as you're only using one text view in the scrollview, but I've also been setting the text in a frame with a calculated 'height' value when I have multiple text views inside the scrollview, otherwise the scroll view seems to 'space' them according to the largest item, creating some wierd visual spacing issues.

Make sure you file a bug report and be very precise with how YOU think it should work. The Apple developers really do pay attention to feedback when they roll out a completely new project like SwiftUI.

Try defining the height of the Scrollview instead of the text.

try this:

struct YourViewName: View {
     @State var content = ""
     var body: some View {
          ScrollView {
               VStack {
                    Text(content)
                         .lineLimit(nil)
               }.frame(maxWidth: .infinity)
          }
     }
}
@Informaticatropoli : Why does this work? maxWidth makes a perfect vertical scrolling view? Makes no sense...

Unfortunately all these tips, July 2021, BigSur 11.4 and the Text refuses to fill its parent's Size. We could really do pixel perfection in AppKit.

struct ContentView : View {
    var message: String
    
    var body: some View {
        ScrollView {
            VStack {
                Text(message)
                    .font(.subheadline)
                    .lineLimit(nil)
                    .background(Color(.sRGB, white: 0.86, opacity: 1))
            }.frame(maxWidth: .infinity)
        }
        .border(Color.red)
    }
}

@kdeda: Do .padding(0) on the VStack, the margins will disappear.

macOS 11.6, Xcode 13.0 (13A233), iOS 15

Looks fixed.

Given:

struct ContentView: View {
   let msg = "Make sure you file a bug report and be very precise with how YOU think it should work. The Apple developers really do pay attention to feedback when they roll out a completely new project like SwiftUI."

   <insert body here>

   ...
   static var previews: some View {
    ObjectImage()
      .previewLayout(.fixed(width: 300, height: 300))
   }

For the following simple implementations, I get:

var body: some View {
  Text(msg)
    .background(Color(.sRGB, white: 0.9))
    .border(Color.red)
}

var body: some View {
  VStack {
    Text(msg)
      .background(Color(.sRGB, white: 0.9))
  }
  .border(Color.red)
}

var body: some View {
  ScrollView {
    VStack {
      Text(msg)
        .background(Color(.sRGB, white: 0.9))
    }
    .border(Color.red)
  }
  .border(Color.purple)
}