Skip to content

Instantly share code, notes, and snippets.

@andresr-dev
Created May 19, 2023 21:09
Show Gist options
  • Save andresr-dev/2ceb0d1122441b1098e5ed6f8d1f7012 to your computer and use it in GitHub Desktop.
Save andresr-dev/2ceb0d1122441b1098e5ed6f8d1f7012 to your computer and use it in GitHub Desktop.
This is a handy container that allows you to specify a width proportion for its subviews
import SwiftUI
/// A view container that overlays its subviews with a given width proportion, aligning them in both axes.
///
/// The `ProportionalZStack` uses a width to specify how much of width proportion its subviews must take.
///
/// The following example creates a `ProportionalZStack` with a proposed width of 70%,
/// an aspectRatio of 4:3 and an offset of 80% for its subviews.
///
/// ```
/// ProportionalZStack(width: 0.7, aspectRatio: 4/3, y: 0.8) {
/// RoundedRectangle(cornerRadius: 15)
/// .fill(.red)
///
/// Text("Hello, World!")
/// .foregroundColor(.white)
/// }
/// ```
struct ProportionalZStack: Layout {
let width: CGFloat
var aspectRatio: CGFloat?
var x: CGFloat
var y: CGFloat
/// Creates a new ProportionalZStack container
/// - Parameters:
/// - width: The proposed width you want for the subviews, 0.0 = 0% and 1.0 = 100% width.
/// - aspectRatio: The ratio of width to height to use for the resulting view.
/// Use `nil` to get a flexible height for the resulting view.
/// - x: The offset `X` for the subviews, 0.0 = 0% and 1.0 = 100% Y. The default value is `0.5` meaning `midX`.
/// - y: The offset `Y` for the subviews, 0.0 = 0% and 1.0 = 100% X. The default value is `0.5` meaning `midY`.
init(width: CGFloat, aspectRatio: CGFloat? = nil, x: CGFloat = 0.5, y: CGFloat = 0.5) {
self.width = width
self.aspectRatio = aspectRatio
self.x = x
self.y = y
}
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
proposal.replacingUnspecifiedDimensions()
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
for subview in subviews {
let subviewWidth = max(10, bounds.width * width)
let subviewHeight = aspectRatio == nil ? bounds.height : subviewWidth / max(0.01, abs(aspectRatio!))
let subviewProposal = ProposedViewSize(width: subviewWidth, height: subviewHeight)
let xPosition = bounds.minX + bounds.width * x
let yPosition = bounds.minY + bounds.height * y
subview.place(at: CGPoint(x: xPosition, y: yPosition), anchor: .center, proposal: subviewProposal)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment