You can achieve this layout using .contentRelativeFrame
and without a GeometryReader
. This was inspired by the approach shown in this video by Stewart Lynch.
import SwiftUI
struct OverviewTiles: View {
//Constants
let ratio: Double = 0.666
let spacing: CGFloat = 16
//Body
var body: some View {
ScrollView {
VStack(spacing: spacing) {
//Row 1
HStack(spacing: spacing) {
Color.blue
.aspectRatio(1, contentMode: .fit)
.containerRelativeFrame(.horizontal) { dimension, _ in
largeWidth(dimension)
}
.cellText("Upcoming Blue", size: .title)
VStack(spacing: spacing) {
Color.cyan
.aspectRatio(1, contentMode: .fit)
.cellText("Blue 1")
Color.cyan
.aspectRatio(1, contentMode: .fit)
.cellText("Blue 2")
}
.containerRelativeFrame(.horizontal, alignment: .trailing) { dimension, _ in
secondaryWidth(dimension)
}
}
//Row 2
HStack(spacing: spacing) {
Color.green
.aspectRatio(2, contentMode: .fit)
.containerRelativeFrame(.horizontal) { dimension, _ in
largeWidth(dimension)
}
.cellText("Upcoming Green", size: .title2)
Color.green
.aspectRatio(1, contentMode: .fit)
.containerRelativeFrame(.horizontal) { dimension, _ in
secondaryWidth(dimension)
}
.cellText("Green 1")
}
//Row 3
Color.orange
.aspectRatio(2.5, contentMode: .fit)
.cellText("Upcoming Orange", size: .title)
}
}
}
private func largeWidth(_ dimension: CGFloat) -> CGFloat {
return dimension * ratio
}
private func secondaryWidth(_ dimension: CGFloat) -> CGFloat {
return (dimension * (1 - ratio)) - spacing
}
}
extension View {
//Modifier function that overlays bottom aligned text with a background
func cellText(_ text: String, size: Font = .body, alignment: Alignment = .bottom) -> some View {
self
.overlay(alignment: .bottom) {
Text(text)
.italic()
.padding(.vertical, 10)
.frame(maxWidth: .infinity, alignment: .center)
.background(.black.opacity(0.5))
.foregroundStyle(.white)
.font(size)
.fontDesign(.serif)
}
}
}
#Preview {
OverviewTiles()
}