I'm working on a layout for a push-up counter app. That is my code and the result so far:
struct ContentView: View {
let sets = [1,3,6,8,12,16,23,43,56,76,100,101,125]
var body: some View {
VStack {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(sets, id: \.self) { set in
Text("\(set)")
.padding(8)
.background(Color.blue)
}
}
}
Color.green
.overlay {
Text("56")
.font(.largeTitle.weight(.bold))
}
}
}
}
The numbers above are sets the user has to perform. I want all the numbers to be in equally sized perfect squares rather than rectangles and the spacing between those squares to be equal. This layout has to dynamically scale and adapt to all font size variants and screen sizes. How can I achieve that in a declarative manner? With no hacks and hard coding.
Conceptually, I want the end result to look like this:
So far, I've tried geometry reader, fixed size modifier, and setting aspect ratio in various places, but everything falls apart.
You can download a sample project here. I appreciate your help!
This requirement can be achieved with a custom
Layout
implementation:In order that the items actually expand to fill the sizes they are given,
.frame(maxWidth: .infinity, maxHeight: .infinity)
needs to be set on the text items. Here is how it comes together:EDIT Following up on your comment in which you asked about a solution for older iOS versions, it would be possible to get near to the same solution by going back to my original solution (using
GeometryReader
) and using anHStack
instead of theLayout
implementation. However, you would have to guess the height that is added to the row. This height would need to be applied to theHStack
as bottom padding. It means the solution doesn't quite fulfil the requirement of no hard-coded values, but maybe it is adequate for the purpose of supporting older iOS versions. The size of the padding could at least be defined as aScaledMetric
so that it adapts to changes to the text size:Then it is used like this: