From what I understood of your question is that:
- You have an
HStack in which the leftmost view is a Rectangle and the rightmost view is a Text.
- You want the
Rectangle to be the same height as the Text.
The problem is that the height of the HStack is based on the tallest child view which happens to be the Rectangle but a Rectangle view does not have any intrinsic size like Text and will occupy all the space the parent provides, or if you manually apply a frame.
You set a width of 20 but leave height and so it takes the entire height it can get.
This indicates that we need to set the height of the Rectangle to be same as the dynamic Text but the problem is that we don't know the height upfront.
To solve this:
- First we need to know the height of the dynamic
Text.
- The height is in a child view so we need it to notify the parent it's height value.
- The parent view should update the
Rectangle when it gets to know the Text height
- A simple
@State variable will suffice now
Solution:
struct ContentLengthPreference: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
struct ContentView: View {
@State var textHeight: CGFloat = 0 // <-- this
var body: some View {
HStack {
Rectangle()
.frame(width: 20, height: textHeight) // <-- this
Text(String(repeating: "lorem ipsum ", count: 25))
.overlay(
GeometryReader { proxy in
Color
.clear
.preference(key: ContentLengthPreference.self,
value: proxy.size.height) // <-- this
}
)
}
.onPreferenceChange(ContentLengthPreference.self) { value in // <-- this
DispatchQueue.main.async {
self.textHeight = value
}
}
}
}
- Create
ContentLengthPreference as our PreferenceKey implementation
- on
Text; Apply overlay containing GeometryReader
overlay will have same height as Text
- in
GeometryReader, Color.clear is just a filler invisible view
anchorPreference modifier allows us to access and store height
onPreferenceChange modifier on parent HStack can catch the value passed by child view
- parent saves the height to a state property
textHeight
textHeight can be applied on Rectangle and will update the view when this value updates
Credits: https://www.wooji-juice.com/blog/stupid-swiftui-tricks-equal-sizes.html
Output (including your header + footer views):

EDIT:
If you have multiple of these in a List then you don't need to do anything. Each row will size automatically upto the Text height.
It's free!!!
struct ContentView: View {
var body: some View {
List(0..<20) { _ in
ArticleView()
}
}
}
struct ArticleView: View {
var body: some View {
VStack(alignment: .leading) {
HStack {
Circle()
.fill(Color.blue)
.frame(width: 15, height: 15)
.overlay(Circle().inset(by: 2).fill(Color.white))
Text("Headline").font(.headline)
}
HStack {
Rectangle().frame(width: 20)
Text(String(repeating: "lorem ipsum ", count: (5...50).randomElement()!))
}
HStack {
Circle()
.fill(Color.orange)
.frame(width: 15, height: 15)
.overlay(Circle().inset(by: 2).fill(Color.white))
Text("Footer").font(.subheadline)
}
}
}
}