UPDATE
If your deployment target at least iOS 16, macOS 13, tvOS 16, or watchOS 9, you can write a custom Layout
. For example:
import SwiftUI
struct MyLayout: Layout {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
return proposal.replacingUnspecifiedDimensions()
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
precondition(subviews.count == 3)
var p = bounds.origin
let h0 = bounds.size.height * 0.43
subviews[0].place(
at: p,
proposal: .init(width: bounds.size.width, height: h0)
)
p.y += h0
let h1 = bounds.size.height * 0.37
subviews[1].place(
at: p,
proposal: .init(width: bounds.size.width, height: h1)
)
p.y += h1
subviews[2].place(
at: p,
proposal: .init(
width: bounds.size.width,
height: bounds.size.height - h0 - h1
)
)
}
}
import PlaygroundSupport
PlaygroundPage.current.setLiveView(MyLayout {
Color.pink
Color.indigo
Color.mint
}.frame(width: 50, height: 100).padding())
Result:
Although this is more code than the GeometryReader
solution (below), it can be easier to debug and to extend to a more complex layout.
ORIGINAL
You can make use of GeometryReader
. Wrap the reader around all other views and use its closure value metrics
to calculate the heights:
let propHeight = metrics.size.height * 0.43
Use it as follows:
import SwiftUI
struct ContentView: View {
var body: some View {
GeometryReader { metrics in
VStack(spacing: 0) {
Color.red.frame(height: metrics.size.height * 0.43)
Color.green.frame(height: metrics.size.height * 0.37)
Color.yellow
}
}
}
}
import PlaygroundSupport
PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())