Skip to content

Instantly share code, notes, and snippets.

@mdb1
Last active October 9, 2023 20:16
Show Gist options
  • Save mdb1/2080d31fe626dd42b17f97d2b9337db6 to your computer and use it in GitHub Desktop.
Save mdb1/2080d31fe626dd42b17f97d2b9337db6 to your computer and use it in GitHub Desktop.
An Adaptable component that switches between HStack and VStack based on the properties and the dynamic type size environment value.
import SwiftUI
/// An adaptable stack view that switches between `HStack` and `VStack` based on the dynamic text size.
struct AdaptableStack<Content>: View where Content: View {
@Environment(\.dynamicTypeSize) private var size: DynamicTypeSize
/// The primary axis along which the stack arranges its children.
private var axis: StackAxis
/// The dynamic type size threshold above which the stack axis will switch.
private var sizeThreshold: DynamicTypeSize
/// Alignment of children along the X-axis for vertical stack.
private var vAlignment: HorizontalAlignment
/// Spacing between children for vertical stack.
private var vSpacing: CGFloat?
/// Alignment of children along the Y-axis for horizontal stack.
private var hAlignment: VerticalAlignment
/// Spacing between children for horizontal stack.
private var hSpacing: CGFloat?
/// Closure providing the stack's content.
private var content: () -> Content
/// Creates an `AdaptableStack` with the given properties.
///
/// - Parameters:
/// - axis: The primary axis for the stack (`vertical` or `horizontal`).
/// - sizeThreshold: The size above which to switch the axis. Default is `.accessibility1`.
/// - vAlignment: The X-axis alignment for a vertical stack. Default is `.center`.
/// - vSpacing: The spacing for a vertical stack. Default is `nil`.
/// - hAlignment: The Y-axis alignment for a horizontal stack. Default is `.center`.
/// - hSpacing: The spacing for a horizontal stack. Default is `nil`.
/// - content: The content of the stack.
init(
_ axis: StackAxis,
sizeThreshold: DynamicTypeSize = .accessibility1,
vAlignment: HorizontalAlignment = .center,
vSpacing: CGFloat? = nil,
hAlignment: VerticalAlignment = .center,
hSpacing: CGFloat? = nil,
@ViewBuilder content: @escaping () -> Content
) {
self.axis = axis
self.sizeThreshold = sizeThreshold
self.vAlignment = vAlignment
self.vSpacing = vSpacing
self.hAlignment = hAlignment
self.hSpacing = hSpacing
self.content = content
}
var body: some View {
layout {
content()
}
}
}
extension AdaptableStack {
/// Enum to represent the primary axis for the stack.
enum StackAxis {
case vertical
case horizontal
}
}
private extension AdaptableStack {
/// Determines the layout to use based on the `axis` and `size`.
var layout: AnyLayout {
switch axis {
case .vertical:
return size > sizeThreshold
? AnyLayout(HStackLayout(alignment: hAlignment, spacing: hSpacing))
: AnyLayout(VStackLayout(alignment: vAlignment, spacing: vSpacing))
case .horizontal:
return size > sizeThreshold
? AnyLayout(VStackLayout(alignment: vAlignment, spacing: vSpacing))
: AnyLayout(HStackLayout(alignment: hAlignment, spacing: hSpacing))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment