Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI not centering when in ZStack

Tags:

swiftui

I am trying to put together a view that consists of a top header view, a bottom content view, and a view that sits on top centered on the line splitting the two views. I figured out I need an alignment guide within a ZStack to position the middle view but I am having problems getting the items in the lower content view centered without a gap.

This code:

extension VerticalAlignment {
    struct ButtonMid: AlignmentID {
        static func defaultValue(in context: ViewDimensions) -> CGFloat {
            return context[.bottom]
        }
    }
    static let buttonMid = VerticalAlignment(ButtonMid.self)
}

struct ContentView: View {
    var body: some View {
        VStack {
            ZStack(alignment: Alignment(horizontal: .center, vertical: .buttonMid)) {
                HeaderView()
                    .frame(maxWidth: .infinity, minHeight: 200, idealHeight: 200, maxHeight: 200, alignment: .topLeading)
                
//                BodyView()
//                    .alignmentGuide(.buttonMid, computeValue: { dimension in
//                        return dimension[VerticalAlignment.top]
//                    })
                
                Color.red
                    .frame(width: 380, height: 50, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                    .alignmentGuide(.buttonMid, computeValue: { dimension in
                        return dimension[VerticalAlignment.center]
                    })
            }
            
            BodyView()
        }
        .edgesIgnoringSafeArea(.top)
    }
}

struct HeaderView: View {
    var body: some View {
        Color.green
    }
}

struct BodyView: View {
    var body: some View {
        VStack {
            Spacer()
            HStack {
                Spacer()
                BodyContent()
                Spacer()
            }
            Spacer()
        }
        .background(Color.blue)
    }
}

struct BodyContent: View {
    var body: some View {
        VStack {
            Text("Line 1")
            Text("Line 2")
            Text("Line 3")
        }
    }
}

give you this:

enter image description here

which centers the lower content they way I want it however it leaves a gap between the upper and lower views. If I uncomment the BodyView code in the ZStack and comment it out in the VStack like so:

struct ContentView: View {
    var body: some View {
        VStack {
            ZStack(alignment: Alignment(horizontal: .center, vertical: .buttonMid)) {
                HeaderView()
                    .frame(maxWidth: .infinity, minHeight: 200, idealHeight: 200, maxHeight: 200, alignment: .topLeading)
                
                BodyView()
                    .alignmentGuide(.buttonMid, computeValue: { dimension in
                        return dimension[VerticalAlignment.top]
                    })
                
                Color.red
                    .frame(width: 380, height: 50, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                    .alignmentGuide(.buttonMid, computeValue: { dimension in
                        return dimension[VerticalAlignment.center]
                    })
            }
            
//            BodyView()
        }
        .edgesIgnoringSafeArea(.top)
    }
}

gives you:

enter image description here

which leaves the content uncentered. How can I keep it centered? I tried putting it in a GeometryReader and that had the same results.

like image 966
GilroyKilroy Avatar asked Aug 30 '25 17:08

GilroyKilroy


1 Answers

You don't need a custom VerticalAlignment. Instead you can put the middle view as an overlay and align it to the top border of the bottom view:

struct ContentView: View {
    var body: some View {
        VStack(spacing: 0) {
            HeaderView()
                .frame(height: 200)
            BodyView()
                .overlay(
                    Color.red
                        .frame(width: 380, height: 50)
                        .alignmentGuide(.top) { $0[VerticalAlignment.center] },
                    alignment: .top
                )
        }
        .edgesIgnoringSafeArea(.top)
    }
}
like image 120
pawello2222 Avatar answered Sep 02 '25 19:09

pawello2222