1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import SwiftUI
/// Lays children out left to right, wrapping to a new row when one won't fit
/// — the shape a row of chips wants. Every row is as tall as its tallest child.
struct FlowLayout: Layout {
var spacing: CGFloat = 8
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize {
let width = proposal.width ?? .infinity
return place(in: width, subviews: subviews).size
}
func placeSubviews(in bounds: CGRect, proposal _: ProposedViewSize, subviews: Subviews, cache _: inout ()) {
let layout = place(in: bounds.width, subviews: subviews)
for (index, origin) in layout.origins.enumerated() {
subviews[index].place(
at: CGPoint(x: bounds.minX + origin.x, y: bounds.minY + origin.y),
proposal: .unspecified
)
}
}
private func place(in width: CGFloat, subviews: Subviews) -> (size: CGSize, origins: [CGPoint]) {
var origins: [CGPoint] = []
var x: CGFloat = 0
var y: CGFloat = 0
var rowHeight: CGFloat = 0
var widest: CGFloat = 0
for subview in subviews {
let size = subview.sizeThatFits(.unspecified)
if x > 0, x + size.width > width {
x = 0
y += rowHeight + spacing
rowHeight = 0
}
origins.append(CGPoint(x: x, y: y))
x += size.width + spacing
rowHeight = max(rowHeight, size.height)
widest = max(widest, x - spacing)
}
return (CGSize(width: widest, height: y + rowHeight), origins)
}
}