Learn Courses My Dashboard

SubView with Geometry Reader Not Centered

I wanted this ErrorView to appear centered on my view. I still find GeometryReaders pretty tricky and I can’t figure out why this ErrorView keeps sticking to the topLeading corner. I’m sure it’s something simple . . . . . help?

struct ErrorView: View {
    
    @State var color = Color.black.opacity(0.7)
    @Binding var alert : Bool
    @Binding var error : String
    
    var body: some View {
        //TODO: Figure out why it's aligning to top left instead of center
        GeometryReader { geo in
            
            VStack {
                
                HStack {
                    
                    Text(self.error == "RESET" ? "Email sent!" : "Error")
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(self.color)
                    
                    Spacer()
                }
                .padding(.horizontal, 25)
                
                Text(self.error == "RESET" ? "Follow the instructions in your email to reset your password." : self.error)
                    .foregroundColor(self.color)
                    .padding(.top)
                    .padding(.horizontal, 25)
                
                Button {
                    
                    self.alert.toggle()
                    
                } label: {
                    Text(self.error == "RESET" ? "Got it!" : "Cancel")
                        .foregroundColor(.white)
                        .padding(.vertical)
                        .frame(width: UIScreen.main.bounds.width - 120)
                }
                .background(Color("pink"))
                .cornerRadius(10)
                .padding(.top, 25)

            }
            .padding(.vertical, 25)
            .frame(width: UIScreen.main.bounds.width - 70)
            .background(Color.white)
            .cornerRadius(15)
            
            
        }
        .background(Color.black.opacity(0.35).edgesIgnoringSafeArea(.all))
        
    }
}

Where ErrorView is instantiated:

    @State var color = Color.black.opacity(0.7)
    @State var email = ""
    @State var password = ""
    @State var isVisible = false
    @Binding var show : Bool
    @State var alert = false
    @State var error = ""
    
    
    var body: some View {
        
        ZStack {

            ZStack(alignment: .topTrailing) {  }
            
            if self.alert {
                // TODO: Is this why error aligns to top left?
                ErrorView(alert: self.$alert, error: self.$error)
            }

        }
    }

Try this:

struct ErrorView: View {

    @State var color = Color.black.opacity(0.7)
    @Binding var alert : Bool
    @Binding var error : String

    var body: some View {
        //TODO: Figure out why it's aligning to top left instead of center
        GeometryReader { geo in

            VStack {

                HStack {

                    Text(error == "RESET" ? "Email sent!" : "Error")
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(self.color)

                    Spacer()
                }
                .padding(.horizontal, 25)

                Text(self.error == "RESET" ? "Follow the instructions in your email to reset your password." : self.error)
                    .foregroundColor(self.color)
                    .padding(.top)
                    .padding(.horizontal, 25)

                Button {

                    alert.toggle()

                } label: {
                    Text(error == "RESET" ? "Got it!" : "Cancel")
                        .foregroundColor(.white)
                        .padding(.vertical)
                        .frame(width: geo.size.width - 120)
                }
                .background(Color.pink)
                .cornerRadius(10)
                .padding(.top, 25)

            }
            .padding()
            .background(Color.white)
            .cornerRadius(15)
            .frame(width: geo.size.width - 70)
            .position(x: geo.size.width / 2, y: geo.size.height / 2)

        }
        .background(Color.black.opacity(0.35).edgesIgnoringSafeArea(.all))
    }
}

It resizes for the selected device so for an iPad you would need to deal with it differently since the Error dialog box will be rather large.

Do you actually even need the GeometryReader? You don’t use it anywhere in your ErrorView.

I agree having looked a lot closer at this code.

struct ErrorView: View {

    @State var color = Color.black.opacity(0.7)
    @Binding var alert : Bool
    @Binding var error : String

    var body: some View {
        //TODO: Figure out why it's aligning to top left instead of center
        ZStack {
            Color.black.opacity(0.35)
                .edgesIgnoringSafeArea(.all)

            VStack(spacing: 20) {

                HStack {
                    Spacer()
                    Text(error == "RESET" ? "Email sent!" : "Error")
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(self.color)

                    Spacer()
                }

                Text(self.error == "RESET" ? "Follow the instructions in your email to reset your password." : self.error)
                    .foregroundColor(self.color)


                Button {

                    alert.toggle()

                } label: {
                    Text(error == "RESET" ? "Got it!" : "Cancel")
                        .bold()
                        .foregroundColor(.white)
                        .padding(.vertical)
                        .frame(width: UIScreen.main.bounds.width - 150)

                }
                .background(Color.pink)
                .cornerRadius(10)

            }
            .padding()
            .background(Color.white)
            .cornerRadius(25)
            .padding(.horizontal, 30)
        }

    }
}

🤦 Shows you how little I understood geometry readers. I don’t actually know how to have different code for different size classes in SwiftUI yet.

For your case use, you really don’t need to use a Geometry Reader. You can configure the message box to be in the middle using the default behaviour of a SwiftUI View.

By default anything you add to a View will be centred. That’s clear to see when you first create a new SwiftUI view where it has “Hello World!” in the middle.

Consider this version of your ErrorView

struct ErrorView: View {

    @State var color = Color.black.opacity(0.7)
    @Binding var alert : Bool
    var error : String

    var body: some View {
        //TODO: Figure out why it's aligning to top left instead of center
        ZStack {
            Color.black.opacity(0.35)
                .edgesIgnoringSafeArea(.all)

            VStack(spacing: 20) {

                Text(error == "RESET" ? "Email sent!" : "Error")
                    .font(.title)
                    .fontWeight(.bold)
                    .foregroundColor(color)

                Text(error == "RESET" ? "Follow the instructions in your email to reset your password." : error)
                    .foregroundColor(color)

                Button {
                    alert.toggle()
                } label: {
                    Text(error == "RESET" ? "Got it!" : "Cancel")
                        .font(.system(size: 20).weight(.bold))
                        .foregroundColor(.white)
                        .padding(.vertical, 15)
                }
                .frame(width: 150)
                .background(Color.pink)
                .cornerRadius(10)
            }
            .padding()
            .frame(maxWidth: .infinity)
            .background(Color.white)
            .cornerRadius(25)
            .padding(.horizontal, 30)
        }

    }
}

Note that there is no need to define the error String as a Binding since you are not changing it inside the ErrorView, rather all you are doing is displaying the contents so define it as a var which means that it is a required parameter when calling the ErrorView.

The VStack in which the 3 elements are placed has a spacing parameter of 20 which gives the contents of the VStack some vertical room to breathe.

The width of the background color can be extended to the edge of the screen by using .frame(maxWidth: .infinity) and then bringing it in by using additional amount of horizontal padding.

Making it a let would be even better.

Ah yeah, minor detail I overlooked.

While we’re at it, there’s no need for color to be an @State property either.