Aha, I see. This is a job for .anchorPreference(). This uses PreferenceKeys, which are a means of sending values back up the view stack to your ancestors. Anchor is an opaque type used to represent view geometry, and they underlie a lot of what makes GeometryReader tick.
What you do is create a PreferenceKey type. This needs a value type (it's generic), a default value, and a function to reduce multiple values down to a single one. An example might be the following, which builds a list containing anchors mapped to tab indices.
struct TabLabelBound: PreferenceKey {
typealias Value = [(tabIdx: Int, bounds: Anchor)]
static var defaultValue: Value = []
func reduce(value: inout Value, next: () -> Value) {
value.append(contentsOf: next())
}
}
You can then use the .anchorPreference() modifier to request an anchor for some part of a view's geometry and transform it to another type, publishing it as a preference to your ancestors:
var body: some View {
MyView()
.anchorPreference(key: TabLabelBound.self, value: .bounds) {
(self.myIndex, bounds: $0) // input is Anchor, convert to TabLabelBound.Value
}
}
Then, in the ancestor view where you've defined the labels you want to underline, you can use the .backgroundPreference() modifier to generate a background view using the value of the anchor in the preference to give it a frame:
SomeTabLabel()
.backgroundPreference(TabLabelBound.self) { prefValue in
GeometryReader { geometry in
// pull out the bounds for the active tab from the prefs
let prefBound = prefValue.first { $0.tabIdx == self.tabIdx }
// GeometryReader's subscript operator turns an anchor into its value type, relative to the current view bounds
let bounds = prefBound ? geometry[prefBound!] : .zero
// For the sake of simplicity, I'm using a rectangle here.
return Rectangle().background(Color.red)
.frame(width: bounds.size.width, height: bounds.size.height) // set bounds
.fixedSize() // don't let it change bounds
.offset(x: bounds.minX, y: bounds.minY) // set position
}
}
This is all off the cuff, but this is basically how these things are done in SwiftUI.
This was courtesy of Javier of swiftui-lab dot com. Link to a more full-featured article there following in a second (moderated) post momentarily.