2

I'm trying to build a view that has Text within a VStack. I'd like the VStack to fill the full width of the screen and I'd like the Text within it to do the same. As far as I can tell the code for that would be as follows:

import SwiftUI

struct Test: View {
    @State private var text = "Hello, World!"

    var body: some View {
        VStack {
            Text(text)
                .frame(maxWidth: .infinity)
                .animation(.default)
            Button(action: {
                self.text = (self.text == "Text") ? "Hello, World!" : "Text"
            }) {
                Text("Toggle")
            }
        }
        .frame(maxWidth: .infinity)
    }
}

struct Test_Previews: PreviewProvider {
    static var previews: some View {
        Test()
    }
}

When I check preview and hover over the text and the stack it shows exactly what I would expect, the Text is full width. However, when I run the app and debug the view hierarchy, it shows that the Text frame has been resized to fit the text. While typically I wouldn't mind this, it results in an animation showing ellipses when the text changes to longer copy. Is it possible to stop Text from sizing to fit the copy?

Steve
  • 871
  • 1
  • 7
  • 17

3 Answers3

2

You may try another animation , like spring:

            Text(text)
            .frame(maxWidth: .infinity)
            .animation(.spring(response: 0.0, dampingFraction:0.2))
E.Coms
  • 9,003
  • 2
  • 14
  • 30
0

Looks like a hard/not possible to fix bug.

You can use this workaround for now until apple give us more access to frame and bounds of the view

Use a disabled TextField instead of Text:

TextField("", text: .constant(text))
    .multilineTextAlignment(.center)
    .disabled(true)
    .frame(maxWidth: .infinity)

Note that this is going to what you described with the full width size, but as you know, string is not animatable, so you will see no animations if there is no change in the frame too.


Use separate objects and fade between them:

Bonus: Smooth fading animation.

struct ContentView: View {
    
    @State private var primaryText = "Hello, World!"
    @State private var alternativeText = "Text"
    @State private var isAlternative = false

    var body: some View {
        VStack {
            ZStack {
                Text(primaryText).opacity(isAlternative ? 0:1)
                Text(alternativeText).opacity(isAlternative ? 1:0)
            }.animation(.default)
            Button("Toggle") {
                self.isAlternative.toggle()
            }
        }
    }
}
Community
  • 1
  • 1
Mojtaba Hosseini
  • 47,708
  • 12
  • 157
  • 176
  • Interesting approach, while this does seem to resolve the sample I placed above it doesn't scale well. For instance, if I were to build a calculator app and wanted to update the text each time you pressed `=` we'd need any number of labels. Seems like SwiftUIs limitation doesn't allow for this to be solved yet. – Steve Nov 22 '19 at 18:17
  • Hmmmm. It would be better if you mention this case in the original question. let me think more about it. – Mojtaba Hosseini Nov 22 '19 at 18:18
  • @Steve if this was helpful, don't forget to use upvote, also if it is the answer to the provided question, please don't forget to mark it as answer. Please feel free to ask a new question about your other issue about scaling a similar approach and I will be there for that ;) Don't forget to link that question here in the comments. – Mojtaba Hosseini Dec 03 '19 at 13:11
-1

Ran into the same problem. Ended up with a pretty hacky solution but does solve it. Please close your eyes. Adding some extra spaces solves the resize problem. :)

Text(on ? "Cooling" : "Off         ")
Pbk
  • 1,180
  • 10
  • 9