iosswiftuivstackhstack

Get perfect alignment and content measurements of HStack and VStack


I'm trying to insert 3 rectangles inside my scrollView precisely 2 horizontally and 1 vertically. I need all three rectangles to respect the same width. Horizontally everything works fine and adapts to all devices instead of the single rectangle vertically I can't find the right way to get the same width result as the horizontal ones

enter image description here

This is the code I used.. can you help me understand if this is the right way to get the result I want or if I have to totally change the way?

var body: some View {

        ScrollView(.vertical) {
            VStack(spacing: 32) {
                Spacer()
                                
                VStack {
                    HStack {
                        ForEach(1...2, id: \.self) { cards in
                            ZStack(alignment: .center) {
                                RoundedRectangle(cornerRadius: 10)
                                    .stroke(lineWidth: 0.5)
                                VStack(alignment: .center, spacing: 16) {
                                    Image(systemName: "megaphone")
                                    Text("Text")
                                    Text("Text")
                                        .multilineTextAlignment(.center)
                                    Image(systemName: "circle")
                                }
                                .padding()
                            }
                        }
                    }

                    ZStack(alignment: .center) {
                        RoundedRectangle(cornerRadius: 10)
                            .stroke(lineWidth: 0.5)
                        VStack(alignment: .center, spacing: 16) {
                            Image(systemName: "megaphone")
                            Text("Text")
                            Text("Text")
                                .multilineTextAlignment(.center)
                            Image(systemName: "circle")
                        }
                        .padding()
                    }
                }
            }
        }
    }

Solution

  • You can use PreferenceKey to extract values from other views. Implement the PreferenceKey method as below:

    struct WidthPreferenceKey: PreferenceKey {
        typealias Value = CGFloat
        static var defaultValue: CGFloat = .zero
        static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {}
    }
    
    struct widthGetter: View {
        var body: some View {
            GeometryReader { geometry in
                Color.clear.preference(key: WidthPreferenceKey.self,
                                       value: geometry.size.width)
            }
        }
    }
    

    Declaration of a variable to store width size:

        @State private var widthSize: CGFloat = 0
    

    After that, you modify the code as follows:

          var body: some View {
    
                ScrollView(.vertical) {
                        VStack(spacing: 32) {
                            Spacer()
                                            
                            VStack {
                                HStack {
                                    ForEach(1...2, id: \.self) { cards in
                                        ZStack(alignment: .center) {
                                            RoundedRectangle(cornerRadius: 10)
                                                .stroke(lineWidth: 0.5)
                                            VStack(alignment: .center, spacing: 16) {
                                                Image(systemName: "megaphone")
                                                Text("Text")
                                                Text("Text")
                                                    .multilineTextAlignment(.center)
                                                Image(systemName: "circle")
                                            }
                                            .padding()
                                        }
                                        .background(widthGetter()) // Add here
                                    }
                                }
    
                                ZStack(alignment: .center) {
                                    RoundedRectangle(cornerRadius: 10)
                                        .stroke(lineWidth: 0.5)
                                    VStack(alignment: .center, spacing: 16) {
                                        Image(systemName: "megaphone")
                                        Text("Text")
                                        Text("Text")
                                            .multilineTextAlignment(.center)
                                        Image(systemName: "circle")
                                    }
                                    .padding()
                                }
                                .frame(width: widthSize) // Add here
                            }
                        }
                }
                .onPreferenceChange(WidthPreferenceKey.self, perform: { widthSize = $0 }) // Add here
            }
    

    You can research more details about PreferenceKey in this link: https://www.youtube.com/watch?v=OnbBc00lqWU&list=PLwvDm4Vfkdphc1LLLjCaEd87BEg07M97y&index=11&ab_channel=SwiftfulThinking