Portion Overlay View
The use of GeometryReader objects imply extra work for SwiftUI, forces SwiftUI to calculate the layout of the view before it can determine the size and position information. This means SwiftUI has to potentially go through multiple layout passes to get the correct dimensions, especially if you are making layout adjustments based on the geometry.
The work mentioned above happens every time the view hierarchy changes or needs to be updated (e.g., due to state changes). SwiftUI will try to minimize the number of layout passes for efficiency. However, introducing GeometryReader can cause additional layout passes because it requires the size and position to be calculated first before the view can be laid out correctly. This can be especially noticeable in dynamic or large lists.
HPortionOverlayModifier ViewModifier offers an alternative when the layout can be defined base on proportions, for example, a circle with diameter equal to one quarter of the view width and be positioned at the second quarter vertically aligned to the bottom. And for these can of uses I created HPortionOverlayModifier and VPortionOverlayModifier
HPortionOverlayModifier
This ViewModifier split the View horizontally in n equal rectangles and then overlay the overlayContent in the specified position .
This modifier may be useful in order to avoid the use of SwiftUI.GeometryReader Object and therefore avoids the additional layout passes and geometry calculations required by this object.
struct HPortionOverlayModifier<T: View>: ViewModifier {
var alignment: Alignment
var divitions: Int
var position: Int
var overlayContent: T
func body(content: Content) -> some View {
content.overlay(alignment: alignment) {
HStack {
ForEach(0..<(position), id: \.self) { _ in
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
}
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
.overlay(alignment: .center) {
overlayContent
}
ForEach(position..<(divitions), id: \.self) { _ in
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
}
}
}
}
}
Of-course when we define a SwiftUI.ViewModifierViewModifier we my want to add an View extension
public
extension View {
func hPortionOverlay<Content: View>(
alignment: Alignment,
divitions: Int,
position: Int,
overlayContent: Content) -> some View {
self.modifier(HPortionOverlayModifier(
alignment: alignment,
divitions: divitions,
position: position,
overlayContent: overlayContent) )
}
}
Preview Examples
struct PortionOverlayView: View {
var body: some View {
Color
.gray
.hPortionOverlay(alignment: .top,
divitions: 4,
position: 4,
overlayContent: Circle().foregroundColor(.green) )
.hPortionOverlay(alignment: .center,
divitions: 5,
position: 1,
overlayContent: Circle().foregroundColor(.red) )
}
}
struct PortionOverlayView_Previews: PreviewProvider {
static var previews: some View {
PortionOverlayView()
}
}

Horizontal and Vertical variants
ViewModifier/PortionOverlayView.swift at iDT-Swift/SwiftUIExt · GitHub
import SwiftUI
struct HPortionOverlayModifier<T: View>: ViewModifier {
var alignment: Alignment
var divitions: Int
var position: Int
var overlayContent: T
func body(content: Content) -> some View {
content.overlay(alignment: alignment) {
HStack {
ForEach(0..<(position), id: \.self) { _ in
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
}
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
.overlay(alignment: .center) {
overlayContent
}
ForEach(position..<(divitions), id: \.self) { _ in
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
}
}
}
}
}
public
extension View {
func hPortionOverlay<Content: View>(
alignment: Alignment,
divitions: Int,
position: Int,
overlayContent: Content) -> some View {
self.modifier(HPortionOverlayModifier(
alignment: alignment,
divitions: divitions,
position: position,
overlayContent: overlayContent) )
}
}
public
struct VPortionOverlayModifier<T: View>: ViewModifier {
let alignment: Alignment
let divitions: Int
let position: Int
let overlayContent: T
public
init(alignment: Alignment, divitions: Int, position: Int, overlayContent: T) {
self.alignment = alignment
self.divitions = divitions
self.position = position
self.overlayContent = overlayContent
}
public
func body(content: Content) -> some View {
content.overlay(alignment: alignment) {
VStack {
ForEach(0..<(position), id: \.self) { _ in
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
}
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
.overlay(alignment: .center) {
overlayContent
}
ForEach(position..<(divitions), id: \.self) { _ in
Rectangle()
.aspectRatio(contentMode: .fit)
.foregroundColor(.clear)
}
}
}
}
}
extension View {
func vPortionOverlay<Content: View>(
alignment: Alignment,
divitions: Int,
position: Int,
overlayContent: Content) -> some View {
self.modifier(VPortionOverlayModifier(
alignment: alignment,
divitions: divitions,
position: position,
overlayContent: overlayContent) )
}
}
struct PortionOverlayView: View {
var body: some View {
Color
.gray
.hPortionOverlay(alignment: .top,
divitions: 4,
position: 4,
overlayContent: Circle().foregroundColor(.green) )
.hPortionOverlay(alignment: .center,
divitions: 5,
position: 1,
overlayContent: Circle().foregroundColor(.red) )
}
}
struct PortionOverlayView_Previews: PreviewProvider {
static var previews: some View {
PortionOverlayView()
}
}
Conclusion
The PortionOverlayView approach offers several advantages over using GeometryReader:
- Performance: By avoiding the extra layout passes required by GeometryReader, this method can improve the overall performance of your SwiftUI views, especially in complex layouts or long lists.
- Simplicity: The API is straightforward and intuitive, allowing developers to think in terms of proportions rather than absolute sizes.
- Flexibility: Both horizontal (
HPortionOverlayModifier) and vertical (VPortionOverlayModifier) variants provide options for different layout needs. - Composability: These modifiers can be easily combined to create complex layouts without nesting GeometryReader views.
By using PortionOverlayView, developers can create efficient, proportion-based layouts in SwiftUI while sidestepping the potential performance pitfalls associated with GeometryReader. This approach is particularly useful for responsive designs that need to adapt to different screen sizes while maintaining relative positioning of elements.