1

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()
                    }
                }
            }
        }
    }
kAiN
  • 2,559
  • 1
  • 26
  • 54

1 Answers1

1

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

baohoang
  • 616
  • 1
  • 5
  • 13
  • Hi sorry I'm having a problem with preferenceKey because I'm using a tabView where I show my views... When I scroll through the views using a "Next" button, the rectangle that changes size thanks to the preferenceKey during the animation doesn't keep its width it is as if preferenceKey is called after the animation and then you notice the change in dimension – kAiN Jun 01 '23 at 18:08
  • Could you explain this to me in more detail? Or give me an example code. Thanks. – baohoang Jun 02 '23 at 02:49
  • In my question the rectangles are entered in a view which I show to the user to enter some data. Data entry is divided into 3 Steps (3 different views). When the user completes the data entry of step 1, he goes to step 2 and then the view is updated with a new one (e.g. step 1 rectanglesView, step 2 textFieldView etc..) To change the views I'm using TabView and every time the user changes step he performs an animation. When the user moves to a new step `preferenceKey` (rectanglesView) it does not keep the right width of the last rectangle so you see the rectangle widening and then shrinking – kAiN Jun 02 '23 at 09:00
  • You can declaration of a variable to store widthSize in the view has TabView `@State private var widthSize: CGFloat = 0`. After that use the Binding variable for all of the views you want set to the same width `@Binding var widthSize: CGFloat`. You can refer to this link: https://quangbao.notion.site/kAiN-9f45abf5a4de4f4d9ed521f751e2c4ae – baohoang Jun 02 '23 at 09:54
  • Ohh.. ok ! thanks ... ! – kAiN Jun 02 '23 at 11:15