Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equal widths of subviews with SwiftUI

I'm trying to build a simple watchOS UI with SwiftUI with two pieces of information side-by-side above a button.

I'd like each side (represented as a VStack within an HStack) to take up half of the available width (so it's an even 50/50 split within the yellow parent view) divided where the | character is centered on the button in the example below.

I want the Short and Longer!!! text to each be centered within each side's 50%.

I started with this code, to get the elements in place and show the bounds of some of the different stacks:

var body: some View {     VStack {         HStack {              VStack {                 Text("Short").font(.body)             }                 .background(Color.green)              VStack {                 Text("Longer!!!").font(.body)             }                 .background(Color.blue)          }             .frame(minWidth: 0, maxWidth: .infinity)             .background(Color.yellow)         Button (action: doSomething) {             Text("|")         }     } } 

Which gave me this result: starting point

Then, when it comes to making each side-by-side VStack 50% of the available width, I'm stuck. I thought it should work to add .relativeWidth(0.5) to each VStack, which should, as I understand it, make each VStack half the width of its parent view (the HStack, with the yellow background):

var body: some View {     VStack {         HStack {              VStack {                 Text("Short").font(.body)             }                 .relativeWidth(0.5)                 .background(Color.green)              VStack {                 Text("Longer!!!").font(.body)             }                 .relativeWidth(0.5)                 .background(Color.blue)          }             .frame(minWidth: 0, maxWidth: .infinity)             .background(Color.yellow)         Button (action: doSomething) {             Text("|")         }     } } 

But this is the result I get: unexpected widths

How can I get the behavior I want with SwiftUI?


Update: After reviewing the SwiftUI documentation more, I see the example here that sets a frame and then defines a relative width in comparison to that frame, so maybe I'm not supposed to use relativeWidth in this way?

I'm a step closer to what I want with the following code:

var body: some View {     VStack {         HStack {              VStack {                 Text("Short").font(.body)             }                 .frame(minWidth: 0, maxWidth: .infinity)                 .background(Color.green)              VStack {                 Text("Longer!!!").font(.body)             }                 .frame(minWidth: 0, maxWidth: .infinity)                 .background(Color.blue)          }             .frame(minWidth: 0, maxWidth: .infinity)             .background(Color.yellow)         Button (action: doSomething) {             Text("|")         }     } } 

which produces this result:

closer to expected result

Now, I am trying to figure out what's creating that extra space in the middle between the two VStacks. So far, experimenting with getting rid of padding and ignoring safe areas does not seem to affect it.

like image 718
gohnjanotis Avatar asked Jun 16 '19 16:06

gohnjanotis


People also ask

How do you make two views the same width or height in SwiftUI?

SwiftUI makes it easy to create two views that are the same size, regardless of whether you want the same height or the same width, by combining a frame() modifier with fixedSize() – there's no need for a GeometryReader or similar.

How many views can HStack SwiftUI have?

HStack can contain up to 10 static views, if you need more static views, you can nest HStack inside another HStack or Group to nest views inside.

Does SwiftUI use Autolayout?

SwiftUI no longer uses Auto Layout, gone all of the cruft introduced over the years. SwiftUI has a completely new layout system designed from the ground up to make it easy to write adaptive cross-platform apps.

What is VStack in Swift?

VStack allows to arrange its child views in a vertical line, and ZStack allows to overlap its child views on top of each other. Stacks can further be customized with alignment and spacing in order to modify their appearance.


2 Answers

I'm still confused about when and how relativeWidth is supposed to be used, but I was able to achieve want I wanted without using it. (EDIT 18 July 2019: According to the iOS 13 Beta 4 release notes, relativeWidth is now deprecated)

In the last update to my question I had some extra spacing between the two sides, and realized that was the default spacing coming in on the HStack and I was able to remove that by setting its spacing to 0. Here's the final code and result:

var body: some View {     VStack {         HStack(spacing: 0) {              VStack {                 Text("Short").font(.body)             }                 .frame(minWidth: 0, maxWidth: .infinity)                 .background(Color.green)              VStack {                 Text("Longer!!!").font(.body)             }                 .frame(minWidth: 0, maxWidth: .infinity)                 .background(Color.blue)          }             .frame(minWidth: 0, maxWidth: .infinity)             .background(Color.yellow)         Button (action: doSomething) {             Text("|")         }     } } 

And here is the result: final result without spacing

like image 137
gohnjanotis Avatar answered Oct 05 '22 22:10

gohnjanotis


You have set background of HStack to yellow color and HStack has some default inter child views spacing. By adding spacing: 0 in HStack will solve the problem see the updated code below.

var body: some View {     VStack {         HStack(spacing: 0) { // Set spacing here             VStack {                 Text("Short").font(.body)             }                 .frame(minWidth: 0, maxWidth: .infinity)                 .background(Color.green)              VStack {                 Text("Longer!!!").font(.body)             }                 .frame(minWidth: 0, maxWidth: .infinity)                 .background(Color.blue)          }             .frame(minWidth: 0, maxWidth: .infinity)             .background(Color.yellow)         Button (action: doSomething) {             Text("|")         }     } } 
like image 39
Online Developer Avatar answered Oct 05 '22 22:10

Online Developer