Skip to content

Instantly share code, notes, and snippets.

@codeactual
Forked from aheze/OverflowLayout.swift
Created July 10, 2023 19:27

Revisions

  1. @aheze aheze created this gist Jul 9, 2023.
    47 changes: 47 additions & 0 deletions OverflowLayout.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,47 @@
    struct OverflowLayout: Layout {
    var spacing = CGFloat(10)

    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
    let containerWidth = proposal.replacingUnspecifiedDimensions().width
    let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
    return layout(sizes: sizes, containerWidth: containerWidth).size
    }

    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
    let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
    let offsets = layout(sizes: sizes, containerWidth: bounds.width).offsets
    for (offset, subview) in zip(offsets, subviews) {
    subview.place(at: CGPoint(x: offset.x + bounds.minX, y: offset.y + bounds.minY), proposal: .unspecified)
    }
    }

    func layout(sizes: [CGSize], containerWidth: CGFloat) -> (offsets: [CGPoint], size: CGSize) {
    var result: [CGPoint] = []
    var currentPosition: CGPoint = .zero
    var lineHeight: CGFloat = 0
    var maxX: CGFloat = 0
    for size in sizes {
    if currentPosition.x + size.width > containerWidth {
    currentPosition.x = 0
    currentPosition.y += lineHeight + spacing
    lineHeight = 0
    }

    result.append(currentPosition)
    currentPosition.x += size.width
    maxX = max(maxX, currentPosition.x)
    currentPosition.x += spacing
    lineHeight = max(lineHeight, size.height)
    }

    return (result, CGSize(width: maxX, height: currentPosition.y + lineHeight))
    }
    }


    /// Usage
    OverflowLayout(spacing: 10) {
    ForEach(badges) { badge in
    Text("Hello!")
    }
    }