57

I'm trying to create a simple stack of Text inside a VStack, and no matter what I do, the text will truncate instead of wrap, even if I explicitly set lineLimit(nil) (although I know this is the default now).

I've tried setting layoutPriority(1) on the first element in the VStack, and I have also tried setting frame(idealHeight: .greatestFiniteMagnitude) as some other posts have suggested, but nothing seems to fix the issue.

Here is a video of the issue in action:

animated gif of bug repro

Here is some code that reproduces the issue:

import SwiftUI

struct BugRepro: View {

    @State var length: Double = 1.0

    var body: some View {
        VStack {
            ForEach(0..<3) { i in
                BugReproElement(index: i)
            }
            .background(Color.gray3)
            .frame(width: UIScreen.main.bounds.width * CGFloat(length))


            Slider(value: $length, in: 0.0...1.0)
        }

    }
}

struct BugRepro_Previews: PreviewProvider {
    static var previews: some View {
        BugRepro()
    }
}

struct BugReproElement: View {
    var index: Int

    var body: some View {
        Text("iaush isuh siudh siudh isudh isudhdsiu sdiuh sdihs")
        .foregroundColor(.gray7)
        .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
    }
}

Could this just be a bug in Xcode? I'm running Beta 7

Adam Singer
  • 2,377
  • 3
  • 18
  • 18

7 Answers7

86

I figured out how to get the text to render properly.

Applying the answer here works: Views compressed by other views in SwiftUI VStack and List

The key is to ensure that the .fixedSize() gets added before .frame()

No Spacer() needed!

Adam Singer
  • 2,377
  • 3
  • 18
  • 18
  • Thanks man, the BEFORE the `frame` was the missing key info for me, but makes sense. – hoshy Sep 11 '19 at 07:41
  • 3
    Also needs to be added before `.font()` – zavidovych Aug 02 '20 at 06:55
  • This also fixes the issue I was having with text fitting into a frame that was *just* wide enough for the text, but wide enough, but still getting truncated. This was *despite using iOS 14, where I was required to use fixedSize: to set a font size in .font. – Snips Jan 14 '21 at 12:45
50

I had to add .fixedSize(horizontal: false, vertical: true) AND .padding() before all my text would stop truncating (show in view properly) I also have a frame tag for a separate vstack (I have 2 separate vstacks in the view)

itsmcgh
  • 783
  • 1
  • 9
  • 12
  • 6
    thank you. the horizontal: false part was very important to make fixedSize() function correctly – bitwit Feb 01 '21 at 23:09
8

Unfortunately It is a bug.

There is a work around you can use to force it to recalculate elements but it's just a workaround and you must wait for the release and see if it fixed.

Workaround

Add a pair of spacer with the height of zero above and below the contents of ForEach.

struct BugRepro: View {

    @State var length: Double = 1.0

    var body: some View {
        VStack {
            ForEach(0..<3) { i in
                Spacer().frame(height: 0)
                BugReproElement(index: i)
                Spacer().frame(height: 0)
            }.frame(width: UIScreen.main.bounds.width * CGFloat(length))

            Slider(value: self.$length, in: 0.0...1.0)
        }
    }
}
Paulw11
  • 108,386
  • 14
  • 159
  • 186
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • This seems to work for some strings but not others. I set the string to be `"125lbs×5reps, 135lbs×3reps, 125lbs×5reps, 125lbs×10reps"` and it again truncates the top line, even with the `Spacer`. (You can try it yourself and remove the `.font` and `.foregroundColor` modifiers and you can still see it) – Adam Singer Sep 05 '19 at 20:35
  • 2
    But I'm going to go with "this is a bug" and move on with the app. – Adam Singer Sep 05 '19 at 20:36
  • If this is of any help, here is a repro of the truncation in a gist: https://gist.github.com/adamsinger/1617f67cab3679fe2c06ac227e29db2b It includes your fixes, but with the updated string. If I preview it using the iPhone XS size, the text is truncated in Xcode 11 beta 7 – Adam Singer Sep 05 '19 at 21:21
  • This definitely fixes this particular example, but doesn't work with certain other strings, and doesn't work when the VStacks are composed with other VStacks. Guess I'll just wait for the next Xcode beta – Adam Singer Sep 07 '19 at 19:51
6

.fixedSized had to be added before .font() for it to work for me.

LondonGuy
  • 10,778
  • 11
  • 79
  • 151
5
Text("Some Text")
    .lineLimit(1)
    .fixedSize()

will display always in 1 line that disaplayed fully.

Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101
0

Common requirement
A common requirement is that the Text should truncate if it needs to because space is tight, but there should be no truncation if there is plenty of space. Unfortunately, it doesn't always work this way and the Text is sometimes truncated even though sufficient space is available.

Preventing truncation brings other problems
Setting .fixedSize is one way to fix this (see other answers here), but this means the text is never truncated. This can cause overflow problems when the text is very long. The solution for overflow is to nest the text in a ScrollView, but this is greedy and grabs all the space available. So this solution is not great when the text is sometimes long but sometimes short.

The breakthrough
After experimenting with turning lots of modifiers on and off, I finally discovered that the text was being truncated unnecessarily when .frame(maxWidth) was being set on the parent container. When this was replaced with .frame(width) and a fixed size, it resolved the issue of unnecessary truncation!

If the max width is unknown then you can surround the parent container with a GeometryReader to find it out.

Benzy Neez
  • 1,546
  • 2
  • 3
  • 10
0

I'll put my two cents here. I found none of these solutions to fix the actual problem. My problem is within the 2nd VStack where the ForEach is. I found that if I just simply wrap the VStack in a Scroll View the other views still constrain to their expected size and padding modifiers. This View is also presented in another view creating a much larger stack hierarchy. Doing this, Parent views and child views continue to respect other padding modifiers while also expanding and setting alignments correctly as expected. Hope this helps someone.

public var body: some View {
    VStack(alignment: alignment) {
        ForEach(lists) { list in
            DisclosureGroup {
                HStack {
                    if alignment == .trailing {
                        Spacer()
                    }
                    ScrollView {
                        VStack(alignment: .leading) {
                            if let header = list.listHeader {
                                header
                                    .font(.body)
                                    .padding(.top)
                            }
                            ForEach(list.listItems) { item in
                                HeaderBody(header: item.header, copy: item.body, alignment: .leading)
                            }
                        }
                    }
                    if alignment == .leading {
                        Spacer()
                    }
                }
            } label: {
                Text(list.listTitle)
                    .font(.body)
                    .bold()
                    .foregroundColor(.purple)
            }
            // Do not show divider on last element
            if let last = lists.last, last.id != list.id {
                Divider()
            }
        }
    }
}
Robin G
  • 1
  • 1