Search code examples
swiftuigeometryreaderswiftui-zstack

How to position a button in lower right of screen in SwiftUI


Consider the following view:

var body: some View {
    
    GeometryReader { geo in
        
        ZStack {
            HStack(spacing:  0) {
                VStack {
                    Text("The number two")
                    Text("2")
                        .font(.largeTitle)
                }
                .frame(maxWidth: .infinity)
                .background(Color.purple)
                
                VStack {
                    Text("The number eight")
                    Text("8")
                        .font(.largeTitle)
                }
                .frame(maxWidth: .infinity)
                .background(Color.green)
                
            }
            
            Button(action: {
                print("Button tapped!")
            }) {
                Text("NEXT")
            }
            .padding()
            .background(Color.orange)
            .foregroundColor(.white)
            .clipShape(RoundedRectangle(cornerRadius:  10))
            .position(x:geo.size.width, y:geo.size.height)
            .padding(16)
            
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(.gray)
        
    }
    
}

On all devices, I want the button positioned in the lower right of the screen, 16 pixels from the trailing edge, and 16 pixels from the bottom edge, regardless of what else is on the screen (in the ZStack). What is the best practice to achieve that? Here's what it looks like now:

enter image description here enter image description here


Solution

  • Your current code is behaving the way it is because position in SwiftUI positions the center of the View. But, you don't actually need position or GeometryReader to do this.

    You can assign a frame to the orange button and align it to bottomTrailing:

    struct ContentView: View {
        var body: some View {
            ZStack {
                HStack(spacing:  0) {
                    VStack {
                        Text("The number two")
                        Text("2")
                            .font(.largeTitle)
                    }
                    .frame(maxWidth: .infinity)
                    .background(Color.purple)
                    
                    VStack {
                        Text("The number eight")
                        Text("8")
                            .font(.largeTitle)
                    }
                    .frame(maxWidth: .infinity)
                    .background(Color.green)
                }
                
                Button(action: {
                    print("Button tapped!")
                }) {
                    Text("NEXT")
                }
                .padding()
                .background(Color.orange)
                .foregroundColor(.white)
                .clipShape(RoundedRectangle(cornerRadius:  10))
                .padding(16)
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing) // <-- Here
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(.gray)
        }
    }