0

I have a set of elements on a HStack that are composed of a circle and some label text underneath it. These elements have a separation between them.

A picture of the elements

Everything works as expected until one of the labels is bigger than the circle. Then, the text increases the actual width of the element, making the visible separation between the circles, off.

enter image description here

Is there any way to evenly separate the circles, ignoring the text?

struct Item: View { let color: Color let label: String var body: some View { VStack { Circle() .fill(color) .frame(width: 40, height: 40) Text(label) } } } struct ItemSeparation: View { var body: some View { HStack(alignment: .top, spacing: 30) { Item(color: .yellow, label: "Berlin") Item(color: .green, label: "Copenhagen") Item(color: .blue, label: "Madrid") Item(color: .purple, label: "Helsinki") } } } 
1
  • You could set the width of all the frames, to the width of the max. Commented Jun 4, 2021 at 17:27

1 Answer 1

1

These two solutions have different compromises. You'll need to decide what happens, for example, if the views will be too wide for the parent view based on the longest label. Option 1 wraps the text. Option 2 makes the view expand beyond its parent.

Option #1:

Use LazyVGrid:

struct ContentView: View { let columns = [ GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible()), ] var body: some View { LazyVGrid(columns: columns, spacing: 30) { Item(color: .yellow, label: "Berlin") Item(color: .green, label: "Copenhagen") Item(color: .blue, label: "Madrid") Item(color: .purple, label: "Helsinki") } } } 

Option #2:

Use a PreferenceKey and a GeometryReader to get the size of the widest View and propagate the changes back up to the parent.

struct Item: View { let color: Color let label: String let width: CGFloat var body: some View { VStack { Circle() .fill(color) .frame(width: 40, height: 40) Text(label) } .border(Color.blue) .background( GeometryReader { Color.clear.preference(key: ViewWidthKey.self, value: $0.size.width) }.scaledToFill() ) .frame(width: width) } } struct ViewWidthKey: PreferenceKey { static var defaultValue: CGFloat { 0 } static func reduce(value: inout Value, nextValue: () -> Value) { let nextValue = nextValue() guard nextValue > value else { return } value = nextValue } } struct ContentView: View { @State private var maxWidth : CGFloat = 0 var body: some View { HStack(alignment: .top, spacing: 30) { Item(color: .yellow, label: "Berlin", width: maxWidth) Item(color: .green, label: "Copenhagen", width: maxWidth) Item(color: .blue, label: "Madrid", width: maxWidth) Item(color: .purple, label: "Helsinki", width: maxWidth) }.onPreferenceChange(ViewWidthKey.self) { width in self.maxWidth = width print(width) } } } 
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the help. Option #2 was precisely what I was looking for. Thank you!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.