59

I have Button

enter image description here

struct ContentView : View {
    var body: some View {
        HStack {
            Button(action: {}) {
                Text("MyButton")
                    .color(.white)
                    .font(Font.system(size: 17))
            }
            .frame(height: 56)
            .background(Color.red, cornerRadius: 0)
        }
    }
}

But I want to pin it to supreview's edges (trailing to superview's trailing and leading). Like this:

enter image description here

HStack doesn't help me, and it's expecting. Fixed frame or width equals UIScree.size are not flexible solutions.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
Argas
  • 1,427
  • 1
  • 10
  • 12

6 Answers6

107

You need to use .frame(minWidth: 0, maxWidth: .infinity) modifier

Add the next code

        Button(action: tap) {
            Text("Button")
                .frame(minWidth: 0, maxWidth: .infinity)
                .background(Color.red)
        }
        .padding(.horizontal, 20)

Padding modifiers will allow you to have some space from the edge.

Keep in mind that the order of modifiers is essential. Because modifiers are functions that are wrapping the view below (they do not change properties of views)

DenFav
  • 2,683
  • 1
  • 18
  • 27
  • 3
    `.infinity` is a nice thing, thank you! But it's imposible to set some instets to superview. But if set .frame(minWidth: 0, maxWidth: .infinity) to `Text`, and add `padding()` to `Button` - it will work! – Argas Jun 08 '19 at 18:19
  • 1
    How can I add specific height for the button? Like if I have to set the button's height as 43px, how can I do that? – Gautam Shrivastav Nov 22 '19 at 12:29
  • 2
    One small addition, is that now you can use the `.padding(.horizontal, 20)` if you want the padding on both sides, without mentioning left and right individually. – perte Feb 05 '20 at 16:27
  • Why minWidth: 0 ? When I remove it I find my view larger than the parent view. I don't understand the logic here – GrandSteph Apr 28 '20 at 13:57
  • .frame(width: UIScreen.main.bounds.size.width - 32 , height: 48, alignment: .center) & dont use padding. This also works – Shrikant Phadke Feb 21 '22 at 13:53
31

You can use GeometryReader: https://developer.apple.com/documentation/swiftui/geometryreader

According to Apple:

This view returns a flexible preferred size to its parent layout.

enter image description here

It is a flexible solution, as it changes according to the parent layout changes.

Marius Waldal
  • 9,537
  • 4
  • 30
  • 44
Matteo Pacini
  • 21,796
  • 7
  • 67
  • 74
7

Here's a pure SwiftUI solution where you want to fill the width of the parent but constrain the height to an arbitrary aspect ratio (say you want square images):

        Image("myImage")
            .resizable()
            .scaledToFill()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .aspectRatio(16.0 / 9.0, contentMode: .fit)

Just replace 16.0 / 9.0 with whatever aspect ratio you want.

I spent about a day trying to figure this out because I didn't want to use GeometryReader or UIScreen.main.bounds (which isn't cross-platform)

Edit: Found an even simpler way.

Sherwin Zadeh
  • 1,339
  • 15
  • 17
5

Simple adding following to your code will make the button extend edge to edge. (You can add padding as well if you want)

.frame(minWidth: 0, maxWidth: .infinity)

The entire Button code will look like this:

struct ContentView : View {
    var body: some View {
        HStack {
            Button(action: {}) {
                Text("MyButton")
                    .color(.white)
                    .font(Font.system(size: 17))
            }
            .frame(minWidth: 0, maxWidth: .infinity)
            .padding(.top, 8)
            .padding(.bottom, 8)
            .background(Color.red, cornerRadius: 0)
        }
    }
}
Tejas
  • 1,050
  • 12
  • 23
2

I found one solution:

var body: some View {
    Button(action: {}) {
            HStack {
                Spacer()
                Text("MyButton")
                    .color(.white)
                    .font(Font.system(size: 17))
                Spacer()
            }
            .frame(height: 56)
            .background(Color.red, cornerRadius: 0)
        }.padding(20)
}

But I'm not sure that it is the best. May be someone will find more elegant solution/

Argas
  • 1,427
  • 1
  • 10
  • 12
  • 1
    One issue here is that the button is tappable outside of its visible bounds. What that means is that the user can incorrectly tap on a button, even if they were tapping on whitespace. – Zorayr May 01 '20 at 02:34
1

iOS 17

From iOS 17, the correct way to do this is to use .containerRelativeFrame modifier because using .infinity width on the frame actually pushes the parent's frame to have an infinite width.

So it would be like:

Button(action: {}) {
    Text("MyButton")
        .foregroundStyle(.white)
        .containerRelativeFrame(.horizontal) //  This should be inside the `label` parameter of the `button`. Otherwise it would be NOT touchable
        .padding(-10) //  This is here to make space to the edges. Yes it should have a NEGATIVE value
}
.frame(height: 56)
.background(.red)
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278