| 2738 | // MARK: - Flow Layout |
| 2739 | |
| 2740 | struct FlowLayout: Layout { |
| 2741 | struct Cache { |
| 2742 | var sizes: [CGSize] = [] |
| 2743 | var positions: [CGPoint] = [] |
| 2744 | var containerSize: CGSize = .zero |
| 2745 | var lastWidth: CGFloat = 0 |
| 2746 | } |
| 2747 | |
| 2748 | var spacing: CGFloat = 8 |
| 2749 | |
| 2750 | func makeCache(subviews: Subviews) -> Cache { |
| 2751 | Cache(sizes: Array(repeating: .zero, count: subviews.count)) |
| 2752 | } |
| 2753 | |
| 2754 | func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) -> CGSize { |
| 2755 | self.arrangeSubviews(proposal: proposal, subviews: subviews, cache: &cache) |
| 2756 | return cache.containerSize |
| 2757 | } |
| 2758 | |
| 2759 | func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) { |
| 2760 | self.arrangeSubviews(proposal: proposal, subviews: subviews, cache: &cache) |
| 2761 | for (index, position) in cache.positions.enumerated() { |
| 2762 | subviews[index].place( |
| 2763 | at: CGPoint(x: bounds.minX + position.x, y: bounds.minY + position.y), |
| 2764 | proposal: .unspecified |
| 2765 | ) |
| 2766 | } |
| 2767 | } |
| 2768 | |
| 2769 | private func arrangeSubviews( |
| 2770 | proposal: ProposedViewSize, |
| 2771 | subviews: Subviews, |
| 2772 | cache: inout Cache |
| 2773 | ) { |
| 2774 | let proposedWidth = proposal.width ?? 0 |
| 2775 | let maxWidth = proposedWidth > 0 ? proposedWidth : 260 |
| 2776 | let needsLayout = cache.positions.count != subviews.count || cache.lastWidth != maxWidth |
| 2777 | |
| 2778 | if needsLayout { |
| 2779 | cache.positions = [] |
| 2780 | cache.positions.reserveCapacity(subviews.count) |
| 2781 | cache.sizes = Array(repeating: .zero, count: subviews.count) |
| 2782 | } |
| 2783 | |
| 2784 | var x: CGFloat = 0 |
| 2785 | var y: CGFloat = 0 |
| 2786 | var rowHeight: CGFloat = 0 |
| 2787 | |
| 2788 | for index in subviews.indices { |
| 2789 | let size: CGSize |
| 2790 | if needsLayout { |
| 2791 | size = subviews[index].sizeThatFits(.unspecified) |
| 2792 | cache.sizes[index] = size |
| 2793 | } else { |
| 2794 | size = cache.sizes[index] |
| 2795 | } |
| 2796 | |
| 2797 | if x + size.width > maxWidth, x > 0 { |
no outgoing calls
no test coverage detected