SwifUI - Set frame on Group of Buttons not working as expected

I want two equally sized buttons. I thought to be efficient using the Group type to set the minWidth and maxWidth on both the buttons, like so:

HStack(spacing: 0) {
    Group {
        Button {
        } label: {
            Text("One")
                .padding()
        }
        .background(.black)
        .tint(.white)
        
        Button {
        } label: {
            Text("Two")
                .padding()
        }
        .background(.white)
        .tint(.black)
    }
    .frame(minWidth: 0, maxWidth: .infinity) // <-
}
.background(.orange)

But this doesn't work. The buttons stay their intrinsic content size. To make them take up half of the space, I have to set the .frame on the buttons individually, like so:

Button {
} label: {
    Text("One")
        .padding()
}
.frame(minWidth: 0, maxWidth: .infinity) // <-
.background(.black)
.tint(.white)

Am I missing some limitations of the Group Type? I thought it would apply .frame() to both?

It certainly does something, because adding it to one Button AND the Group, gives desired behavior for that one Button already.

Answered by BabyJ in 765257022

The issue is not with the Group but with the order of the modifiers. The order is very important in SwiftUI as it dictates how views are rendered and can produce different outcomes by just flipping two modifiers around. In your case, you are applying the frame modifier after the background modifier which is why you are seeing this result:

To explain, these two snippets are effectively the same and demonstrates what Group is doing:

Group {
    Button {
    } label: {
        Text("One")
            .padding()
    }
    .background(.black)
    .tint(.white)

    Button {
    } label: {
        Text("Two")
            .padding()
    }
    .background(.white)
    .tint(.black)
}
.frame(minWidth: 0, maxWidth: .infinity) // <-
Button {
} label: {
    Text("One")
        .padding()
}
.background(.black)
.tint(.white)
.frame(minWidth: 0, maxWidth: .infinity) // <-

Button {
} label: {
    Text("Two")
        .padding()
}
.background(.white)
.tint(.black)
.frame(minWidth: 0, maxWidth: .infinity) // <-


What you really want is this:

which can be achieved by applying the frame modifier before the background modifier, like this:

Button {
} label: {
    Text("One")
        .padding()
}
.frame(minWidth: 0, maxWidth: .infinity) // <-
.background(.black)
.tint(.white)

Button {
} label: {
    Text("Two")
        .padding()
}
.frame(minWidth: 0, maxWidth: .infinity) // <-
.background(.white)
.tint(.black)

This has to be done individually as using Group can't fulfil that. What you can do instead is create a custom button style with this max frame (and anything else you want) and then apply that to the Group or HStack.

Accepted Answer

The issue is not with the Group but with the order of the modifiers. The order is very important in SwiftUI as it dictates how views are rendered and can produce different outcomes by just flipping two modifiers around. In your case, you are applying the frame modifier after the background modifier which is why you are seeing this result:

To explain, these two snippets are effectively the same and demonstrates what Group is doing:

Group {
    Button {
    } label: {
        Text("One")
            .padding()
    }
    .background(.black)
    .tint(.white)

    Button {
    } label: {
        Text("Two")
            .padding()
    }
    .background(.white)
    .tint(.black)
}
.frame(minWidth: 0, maxWidth: .infinity) // <-
Button {
} label: {
    Text("One")
        .padding()
}
.background(.black)
.tint(.white)
.frame(minWidth: 0, maxWidth: .infinity) // <-

Button {
} label: {
    Text("Two")
        .padding()
}
.background(.white)
.tint(.black)
.frame(minWidth: 0, maxWidth: .infinity) // <-


What you really want is this:

which can be achieved by applying the frame modifier before the background modifier, like this:

Button {
} label: {
    Text("One")
        .padding()
}
.frame(minWidth: 0, maxWidth: .infinity) // <-
.background(.black)
.tint(.white)

Button {
} label: {
    Text("Two")
        .padding()
}
.frame(minWidth: 0, maxWidth: .infinity) // <-
.background(.white)
.tint(.black)

This has to be done individually as using Group can't fulfil that. What you can do instead is create a custom button style with this max frame (and anything else you want) and then apply that to the Group or HStack.

SwifUI - Set frame on Group of Buttons not working as expected
 
 
Q