Swift MIT

SwiftUI-Flow

Flow Layout implemented in SwiftUI

T

tevelee

Dernière activité 18 août 2026
tevelee/SwiftUI-Flow

741

étoiles

64

forks

0

issues ouvertes

flow-layoutlayoutswiftui

Ce README est souvent en anglais.

SwiftUI Flow Layout

CI codecov Swift Package Index versions Supported platforms License

Introduces HFlow and VFlow similar to HStack and VStack. Arranges views in lines and cuts new lines accordingly (if elements don't fit the bounding space).

Features

  • 📐 Wrapping layout — items flow onto new lines (or columns) when they run out of space.
  • ↔️ Two axes — HFlow wraps rows, VFlow wraps columns.
  • 🎯 Per-axis alignment — align items within a line and lines within the layout.
  • ↕️ Independent spacing — set item spacing and line spacing separately.
  • ⚖️ Even distribution — balance items across lines with the Knuth–Plass algorithm.
  • ↔️ Justified lines — stretch lines to fill the available space.
  • 🪗 Flexibility model — let items grow, stay rigid, or claim a whole line.
  • 📏 Line limits — cap a flow to N lines with an optional "+N more" overflow indicator.
  • ➗ Separators — draw dividers between items and/or lines (never at the edges); they take part in sizing and wrapping.
  • ✂️ Manual line breaks — force breaks with LineBreak() or .startInNewLine().
  • 🌍 RTL support — adapts to the environment's layout direction.
  • 🧱 Layout conformance — HFlowLayout / VFlowLayout for use anywhere a Layout is expected.

Requirements

  • iOS 16+, macOS 13+, tvOS 16+, watchOS 9+, visionOS 1+
  • Swift 5.9+

Installation

Add the package to your Package.swift:

.package(url: "https://github.com/tevelee/SwiftUI-Flow", from: "3.1.1")

then add "Flow" to your target's dependencies. In Xcode, choose File ▸ Add Package Dependencies… and enter https://github.com/tevelee/SwiftUI-Flow.

Documentation

Full API reference and guides are hosted on the Swift Package Index.

HFlow

import Flow

struct Colors: View {
    let colors: [Color] = [
        .blue,
        .orange,
        .green,
        .yellow,
        .brown,
        .mint,
        .indigo,
        .cyan,
        .gray,
        .pink
    ]

    var body: some View {
        HFlow {
            ForEach(colors, id: \.description) { color in
                RoundedRectangle(cornerRadius: 10)
                    .fill(color.gradient)
                    .frame(width: .random(in: 40...60), height: 50)
            }
        }
        .frame(maxWidth: 300)
    }
}

HFlow

VFlow

VFlow {
    ForEach(colors, id: \.description) { color in
        RoundedRectangle(cornerRadius: 10)
            .fill(color.gradient)
            .frame(width: 50, height: .random(in: 40...60))
    }
}
.frame(maxHeight: 300)

VFlow

Alignment

Supports the same alignments as HStack and VStack do.

HFlow(alignment: .top) {
    ForEach(colors, id: \.description) { color in
        RoundedRectangle(cornerRadius: 10)
            .fill(color.gradient)
            .frame(width: 50, height: .random(in: 40...60))
    }
}
.frame(maxWidth: 300)

HFlow

Additionally, alignment can be specified on both axes. Ideal for tags.

HFlow(horizontalAlignment: .center, verticalAlignment: .top) {
    ForEach(colors, id: \.description) { color in
        RoundedRectangle(cornerRadius: 10)
            .fill(color.gradient)
            .frame(width: .random(in: 30...60), height: 30)
    }
}
.frame(maxWidth: 300)

HFlow

Spacing

Customize spacing between rows and items separately.

HFlow(itemSpacing: 4, rowSpacing: 20) {
    ForEach(colors, id: \.description) { color in
        RoundedRectangle(cornerRadius: 10)
            .fill(color.gradient)
            .frame(width: 50, height: 50)
    }
}
.frame(maxWidth: 300)

HFlow

Distribute items

Distribute items evenly by minimizing the empty spaces left in each row. Implements the Knuth-Plass line breaking algorithm.

HFlow(distributeItemsEvenly: true) {
    ForEach(colors, id: \.description) { color in
        RoundedRectangle(cornerRadius: 10)
            .fill(color.gradient)
            .frame(width: 65, height: 50)
    }
}
.frame(width: 300, alignment: .leading)
.border(.gray)

HFlow

Justified

HFlow(justified: true) {
    ForEach(colors, id: \.description) { color in
        RoundedRectangle(cornerRadius: 10)
            .fill(color.gradient)
            .frame(width: 50, height: 50)
    }
}
.frame(width: 300)

HFlow

Flexibility

HFlow { // distributes flexible items proportionally
    RoundedRectangle(cornerRadius: 10)
        .fill(.red)
        .frame(minWidth: 50, maxWidth: .infinity)
        .frame(height: 50)
        .flexibility(.minimum) // takes as little space as possible, rigid
    RoundedRectangle(cornerRadius: 10)
        .fill(.green)
        .frame(minWidth: 50, maxWidth: .infinity)
        .frame(height: 50)
        .flexibility(.natural) // expands
    RoundedRectangle(cornerRadius: 10)
        .fill(.blue)
        .frame(minWidth: 50, maxWidth: .infinity)
        .frame(height: 50)
        .flexibility(.natural) // expands
    RoundedRectangle(cornerRadius: 10)
        .fill(.yellow)
        .frame(minWidth: 50, maxWidth: .infinity)
        .frame(height: 50) // takes as much space as possible
        .flexibility(.maximum)
}
.frame(width: 300)

HFlow

Use .grow(_:) to distribute leftover space proportionally by weight:

HFlow {
    RoundedRectangle(cornerRadius: 8).fill(.orange)
        .frame(minWidth: 40, maxWidth: .infinity, minHeight: 44, maxHeight: 44)
        .flexibility(.grow(2))  // takes 2/3 of leftover space

    RoundedRectangle(cornerRadius: 8).fill(.teal)
        .frame(minWidth: 40, maxWidth: .infinity, minHeight: 44, maxHeight: 44)
        .flexibility(.grow(1))  // takes 1/3 of leftover space
}

Line limits

Cap a flow to a maximum number of lines with .maxLines(_:):

HFlow {
    ForEach(tags) { tag in TagView(tag) }
}
.maxLines(2)

Add a trailing overflow indicator using the closure overload (available on HFlow and VFlow):

HFlow {
    ForEach(tags) { tag in TagView(tag) }
}
.maxLines(2) { hidden in
    Text("+\(hidden) more").foregroundStyle(.secondary)
}

HFlow

Separators

HFlow {
    ForEach(tags, id: \.self) { Text($0) }
}
.itemSeparator { Text("•").foregroundStyle(.secondary) }
.lineSeparator { Divider() }

HFlow

Line breaks

HFlow {
    RoundedRectangle(cornerRadius: 10)
        .fill(.red)
        .frame(width: 50, height: 50)
    RoundedRectangle(cornerRadius: 10)
        .fill(.green)
        .frame(width: 50, height: 50)
    RoundedRectangle(cornerRadius: 10)
        .fill(.blue)
        .frame(width: 50, height: 50)
    LineBreak() // <--
    RoundedRectangle(cornerRadius: 10)
        .fill(.yellow)
        .frame(width: 50, height: 50)
}
.frame(width: 300)

HFlow

HFlow {
    RoundedRectangle(cornerRadius: 10)
        .fill(.red)
        .frame(width: 50, height: 50)
    RoundedRectangle(cornerRadius: 10)
        .fill(.green)
        .frame(width: 50, height: 50)
        .startInNewLine() // <-- 
    RoundedRectangle(cornerRadius: 10)
        .fill(.blue)
        .frame(width: 50, height: 50)
    RoundedRectangle(cornerRadius: 10)
        .fill(.yellow)
        .frame(width: 50, height: 50)
}
.frame(width: 300)

HFlow

RTL

Adapts to left-to-right and right-to-left environments too.

HFlow {
    ForEach(colors, id: \.description) { color in
        RoundedRectangle(cornerRadius: 10)
            .fill(color.gradient)
            .frame(width: .random(in: 40...60), height: 50)
    }
}
.frame(maxWidth: 300)
.environment(\.layoutDirection, .rightToLeft)

HFlow

Projets similaires

SwiftUI views that arrange their children in a flow layout.

Swiftswiftswiftui
Cciaranrobrien
174 étoiles11

Reframing SwiftUI Views. A collection of tools to help with layout.

Swiftlayoutswiftswiftui
Rryanlintott
313 étoiles7

FlowStacks allows you to hoist SwiftUI navigation and presentation state into a Coordinator

Swift
Jjohnpatrickmorgan
969 étoiles71