Skip to content

Instantly share code, notes, and snippets.

@sebj
Last active August 8, 2023 10:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebj/439485e1f8c6bdf9be9d1001446d88fd to your computer and use it in GitHub Desktop.
Save sebj/439485e1f8c6bdf9be9d1001446d88fd to your computer and use it in GitHub Desktop.
A container that produces different combinations of children as part of a given `Layout`, by incrementally dropping the lowest priority child. See https://movingparts.io/variadic-views-in-swiftui & https://chris.eidhof.nl/post/variadic-views/
import SwiftUI
struct ContentView: View {
var body: some View {
HStack(spacing: 20) {
ViewThatFits(in: .vertical) {
ChildCombinations(in: VStackLayout(spacing: 0)) {
TestSquare(color: .red)
.combinationPriority(2)
TestSquare(color: .blue)
.combinationPriority(1)
TestSquare(color: .green)
// .combinationPriority(0) is implicit
}
}
.frame(height: 200)
ViewThatFits(in: .vertical) {
ChildCombinations(in: VStackLayout(spacing: 0)) {
TestSquare(color: .red)
// .combinationPriority(0) is implicit
TestSquare(color: .blue)
.combinationPriority(1)
TestSquare(color: .green)
.combinationPriority(2)
}
}
.frame(height: 200)
}
}
}
struct TestSquare: View {
let color: Color
var body: some View {
Rectangle()
.fill(color)
.frame(width: 100, height: 100)
}
}
// MARK: -
/// A container that produces different combinations of children as part of a given `Layout`, by incrementally dropping the last child.
struct ChildCombinations<Content>: View where Content: View {
init<L>(in layout: L, @ViewBuilder content: () -> Content) where L: Layout {
self.layout = AnyLayout(layout)
self.content = content()
}
private let layout: AnyLayout
private var content: Content
var body: some View {
_VariadicView.Tree(
ViewCombinationsVariadicRoot(layout: layout)
) {
content
}
}
}
private struct CombinationPriorityTraitKey: _ViewTraitKey {
static var defaultValue: Int = 0
}
extension View {
/// Sets the priority by which a parent layout should include this child in combinations of its children.
func combinationPriority(_ value: Int) -> some View {
_trait(CombinationPriorityTraitKey.self, value)
}
}
/// Produces different combinations of children, by incrementally dropping the last child.
private struct ViewCombinationsVariadicRoot: _VariadicView.MultiViewRoot {
let layout: AnyLayout
@ViewBuilder
func body(children: _VariadicView.Children) -> some View {
let childIDsSortedByPriority = children
.sorted(by: { a, b in a[CombinationPriorityTraitKey.self] < b[CombinationPriorityTraitKey.self] })
.map(\.id)
ForEach(Array(zip(children.indices, children)), id: \.1.id) { index, _ in
layout {
let droppedChildIDs = childIDsSortedByPriority[..<index]
let highestPriorityChildren = children.filter { !droppedChildIDs.contains($0.id) }
ForEach(highestPriorityChildren) { child in
child
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment