Home > Enterprise >  Why does adding a HStack inside a VStack wreck size calculations for a ZStack bubble around text ele
Why does adding a HStack inside a VStack wreck size calculations for a ZStack bubble around text ele

Time:11-11

I want to produce a bubble-like view with various pieces of text on a rounded rectangle background, sized to fit the text. I have arrived at this:

    var body: some View {
        HStack {
            Spacer()
            ZStack(alignment: .trailing) {
                Color.blue
                VStack{
                    Text(message.content)
                        .padding(.all, .oneUnit)
                        .multilineTextAlignment(.trailing)
                }
            }
            .clipShape(RoundedRectangle(cornerRadius: 12))
        }
    }

When that is repeated with a ForEach and some text passed in as message, content is almost right but the ZStack background should only be big enough to cover the text, making a bubble:

ZStack is too large - full width though text is small

Using .layoutPriority fixes this, code becomes:

    var body: some View {
        HStack {
            Spacer()
            ZStack(alignment: .trailing) {
                Color.blue
                VStack{
                    Text(message.content)
                        .padding(.all, .oneUnit)
                        .multilineTextAlignment(.trailing)
                }
                .layoutPriority(1)
            }
            .clipShape(RoundedRectangle(cornerRadius: 12))
        }
    }

Correct size - fits text

So the size of the ZStack is the size required to fit the text. Great! Now to put all the required pieces of text in the VStack. Let's add one more Text:

    var body: some View {
        HStack {
            Spacer()
            ZStack(alignment: .trailing) {
                Color.blue
                VStack{
                    Text(message.content)
                        .padding(.all, .oneUnit)
                        .multilineTextAlignment(.trailing)
                    Text("Out damned spot!")
                }
                .layoutPriority(1)
            }
            .clipShape(RoundedRectangle(cornerRadius: 12))
        }
    }

Text display underneath, sizing still correct.

VStack with two text elements is correctly sized

But that last piece of text has to be right-aligned. So then the code is:

    var body: some View {
        HStack {
            Spacer()
            ZStack(alignment: .trailing) {
                Color.blue
                VStack{
                    Text(message.content)
                        .padding(.all, .oneUnit)
                        .multilineTextAlignment(.trailing)
                    HStack {
                        Spacer()
                        Text("Out damned spot!")
                    }
                }
                .layoutPriority(1)
            }
            .clipShape(RoundedRectangle(cornerRadius: 12))
        }
    }

And then everything is broken. Can anyone say why the HStack breaks the ZStack's size?

ZStack becomes full-width - incorrect!

CodePudding user response:

@SwissMark is right, the cause of your problem is that Spacer() is greedy, it takes all the space left. Making your bubbles the max size possible. But only making VStack alignment .trailing doesn't do the trick.

Problems:

  1. Your background is Set in the ZStack
  2. layoutPriority doesn't make any sense here

Here is the solution:

import SwiftUI

struct ContentView: View {
    
    var messages = [
        "I heard the owl scream and the crickets cry.",
        "Out damned spot!",
        "Did not you speak?",
        "Out damned spot!",
        "When?",
        "Out damned spot!",
        "Now.",
        "Out damned spot!"
    ]
    
    var body: some View {
        HStack {
            Spacer()
            ZStack(alignment: .trailing) {
                VStack(alignment: .trailing) {
                    VStack(alignment: .trailing) {
                        Text(messages[0])
                            .padding(.all, 4)
                            .multilineTextAlignment(.trailing)
                        Text(messages[1])
                            .padding([.bottom, .trailing], 4)
                    }
                    .padding(4)
                    .background(Color.blue)
                    .clipShape(RoundedRectangle(cornerRadius: 12))
                    
                    VStack(alignment: .trailing) {
                        Text(messages[2])
                            .padding(.all, 4)
                            .multilineTextAlignment(.trailing)
                        Text(messages[3])
                            .padding([.bottom, .trailing], 4)
                    }
                    .padding(4)
                    .background(Color.blue)
                    .clipShape(RoundedRectangle(cornerRadius: 12))
                    
                    VStack(alignment: .trailing) {
                        Text(messages[4])
                            .padding(.all, 4)
                            .multilineTextAlignment(.trailing)
                        Text(messages[5])
                            .padding([.bottom, .trailing], 4)
                    }
                    .padding(4)
                    .background(Color.blue)
                    .clipShape(RoundedRectangle(cornerRadius: 12))
                    
                    VStack(alignment: .trailing) {
                        Text(messages[6])
                            .padding(.all, 4)
                            .multilineTextAlignment(.trailing)
                        Text(messages[7])
                            .padding([.bottom, .trailing], 4)
                    }
                    .padding(4)
                    .background(Color.blue)
                    .clipShape(RoundedRectangle(cornerRadius: 12))
                }
            }
        }
    }
}

Final image:

enter image description here

Modify the paddings and spaces as you wish.

  • Related