Reputation: 2957
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:
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
Upvotes: 77
Views: 48730
Reputation: 11
I found a reasonable workaround:
Text("Some Text")
.lineLimit(1)
.minimumScaleFactor(0.99)
This small adjustment to the scale factor, set to just below 1, is hardly noticeable but seems to effectively rectify the issue with text truncation in an HStack
. It likely alters the way SwiftUI manages the resizing of text content. When the text has enough room, it is displayed at its full size. If not, the text slightly scales down before it gets truncated. This method tweaks the resizing strategy, ensuring that the text stays readable for as long as possible.
The issue with .fixedSize()
is that you lose the truncation mechanism, it is critical whenever you support several languages as the text might become really long. My solution solves the issue and maintains the truncation behavior.
Upvotes: 1
Reputation: 1
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()
}
}
}
}
Upvotes: 0
Reputation: 20769
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.
Upvotes: 3
Reputation: 11098
.fixedSized
had to be added before .font()
for it to work for me.
Upvotes: 7
Reputation: 11426
Text("Some Text")
.lineLimit(1)
.fixedSize()
will display always in 1 line that disaplayed fully.
Upvotes: 7
Reputation: 923
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)
Upvotes: 63
Reputation: 119302
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)
}
}
}
Upvotes: 8
Reputation: 2957
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!
Upvotes: 112