Last active
August 8, 2023 10:08
-
-
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/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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