EN VI

SwiftUI overlay alignment for horizontal centering?

2024-03-16 02:30:07
How to SwiftUI overlay alignment for horizontal centering

I need view A to be centered on the screen (vertically and horizontally). I need view B to appear 30px below view A, also centered horizontally. But A must remain in the center of the screen, without moving upward when B is added. I can accomplish most of this by using .overlay and GeometryReader:

struct ContentView: View {
        
    var body: some View {
        VStack {
            ZStack {
                Rectangle()
                    .fill(.red)
                    .frame(width: 200, height: 100)
                    .overlay {
                        GeometryReader {geo in
                            Rectangle()
                                .fill(.blue)
                                .frame(width: 100, height: 100)
                                .offset(y: geo.size.height + 30)
                        }
                    }
            }
        }
    }
}

enter image description here

I don't know how to center the blue Rectangle horizontally though. I don't necessarily know its width, although it's specified here for visual purposes. Assuming I don't know the exact frame values for either view, how I can I position B 30px below A while keeping A in the center of the screen? And is there a better view structure for accomplishing this?

Solution:

You could just use a VStack with a hidden copy of the blue rectangle above the red one:

struct ContentView: View {
    
    private var blueRectangle: some View {
        Rectangle()
            .fill(.blue)
            .frame(width: 100, height: 100)
    }
    
    var body: some View {
        VStack(spacing: 30) {
            blueRectangle.hidden()
            Rectangle()
                .fill(.red)
                .frame(width: 200, height: 100)
            blueRectangle
        }
    }
}

Screenshot

Alternatively, if the blue rectangle is not expected to be wider than the red one then here's how you could do it with an overlay:

var body: some View {
    Rectangle()
        .fill(.red)
        .frame(width: 200, height: 100)
        .overlay(alignment: .top) {
            GeometryReader { proxy in
                blueRectangle
                    .frame(maxWidth: proxy.size.width)
                    .fixedSize(horizontal: false, vertical: true)
                    .offset(y: proxy.size.height + 30)
            }
        }
}
Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login