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:

  1. 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.
  2. Simplicity: The API is straightforward and intuitive, allowing developers to think in terms of proportions rather than absolute sizes.
  3. Flexibility: Both horizontal (HPortionOverlayModifier) and vertical (VPortionOverlayModifier) variants provide options for different layout needs.
  4. 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.