chvolow24
chvolow24

Reputation: 280

Responsive text box with GeometryReader

I'm trying to create responsive changes to some view parameters (e.g. font size, container box size) based on how much of a given space a text element is occupying at a given moment. The text must also be pushed to the right side of the space, which I'm accomplishing by grouping it with a Spacer() inside an HStack. Because of the space-greediness (and a related behavior I don't understand) of GeometryReader, I'm having a hard time getting the sizing variables I need without breaking the intended structure and behavior of the view.

Here's a version of the view without the responsive changes. The text appears where it should within the rounded rectangle:

struct responsiveTextBox: View {
    @State var text = "Hi. "
    var body: some View {
        VStack {
            ZStack{
                RoundedRectangle(cornerRadius: 5)
                    .stroke()
                HStack {
                    Spacer()
                    Text(text)
                }
            }
            .frame(width: 300, height: 100)
            Button("Add text") {
                text += "Hi. "
            }
            Button("Erase text") {
                text = ""
            }
        }
    }
}

Appearance of view with desired position of text

In order to implement functionality that responds to the relative amount of space occupied by the text, I need to know the size of either the Spacer() element or the Text() element itself. Wrapping the Spacer() in a GeometryReader doesn't work, because unlike the Spacer alone, the GeometryReader won't shrink to less than 50% of the HStack's width:

[...]
HStack {
    GeometryReader() {freeSpace in
        Spacer()
    }
    Text(text)
}
[...]

Effect of wrapping Spacer in GeometryReader

I don't really understand this behavior.

The alternative - wrapping the Text() element in a GeometryReader - also doesn't work, because the GeometryReader's space-greediness overrides the Spacer's, and the text appears on the left side of the rounded rectangle.

HStack {
    Spacer()
    GeometryReader() {textSpace in
        Text(text)
    }
}

Effect of wrapping Text in GeometryReader

What's the right way to do this?

Upvotes: 0

Views: 372

Answers (1)

ChrisR
ChrisR

Reputation: 12125

In cases like these you can use GeometryReader in an overlay or background of the element you are interested in – so it does not affect the layout.

IMHO you also don't need the Spacer or HStack, just set the alignment on ZStack.

And ultimately you might want to look into the new Layout protocol. It seems to be the way to go for what you want to achieve. Example here

struct ContentView: View {
    
    @State var text = "Hi. "
    @State var textSize = CGSize.zero
    
    var body: some View {
        VStack {
            ZStack(alignment: .trailing) {
                RoundedRectangle(cornerRadius: 5)
                    .stroke()
                Text(text)
                    .id(text)
                    .background( GeometryReader { geo in Color.clear.onAppear { textSize = geo.size }})
            }
            .frame(width: 300, height: 100)
            
            Button("Add text") {
                text += "Hi. "
            }
            Button("Erase text") {
                text = ""
            }
            Text("Width: \(textSize.width), Height: \(textSize.height)")
        }
    }
}

Upvotes: 1

Related Questions