Adaptive vertical grid layout for SwiftUI


Swift Package Manager compatible

A SwiftUI Package for arranging subviews in an width-adaptive grid pattern expanding vertically, such as for a set of Tags.

At first, it seemed like the built-in LazyVStack could do it, but although it can make adaptive columns, that size is fixed for all rows.

Minimum target is macOS 13 or iOS 16 (for Layout).

1. Layout Example

import SwiftUI
import AdaptiveGridLayout

struct SampleView: View {

    let itemHeight: CGFloat = 50

    @State var items: [(width: CGFloat, color: Color)] = (0..<40).map { _ in
        (CGFloat.random(in: 10..<120),
         Color(hue: .random(in: 0..<1.0),
               saturation: .random(in: 0.1..<0.8),
               brightness:.random( in: 0.5..<0.9))

    var body: some View {
        AdaptiveVGrid(spacing: 2) { // <------------

            ForEach(items.indices, id: \.self) { index in
                    .frame(width: items[index].width, height: itemHeight)
                    .overlay { Text(index, format: .number) }


Adaptive Layout Sample 1

2. TagsView Example

import SwiftUI
import AdaptiveGridLayout

// MARK: View

struct TagsView: View {
    var model = FoodModel()
    var body: some View {
        AdaptiveVGrid(spacing: 6) {
            ForEach(model.fruits) { fruit in
                TagView(word: LocalizedStringKey(
    struct TagView: View {
        let word: LocalizedStringKey
        var body: some View {
                .padding(.vertical, 4)
                .background(, in: Capsule(style: .circular))

// MARK: Model

class FoodModel {
    var fruits: [Fruit] = ["Tangerine", "Honeydew", "Fig", "Zucchini", "Orange", "Cherry", "Papaya", "Dragon Fruit", "Dates", "Lemon", "Apple", "Nectarine", "Raspberry", "Banana"]

    struct Fruit: Identifiable, ExpressibleByStringLiteral {
        let name: String
        var id: String { name }
        init(stringLiteral value: StringLiteralType) {
            name = value
Adaptive Layout Example 2


Elements handle their own animation. The easiest way to add some animation to adding and removing elements is with matchedGeometry. For the sample above, add .matchedGeometryEffect(id:, in: namespace) to each element then .animation(.spring, value: model.fruits).



  • Swift Tools 5.9.0
View More Packages from this Author


  • None
Last updated: Mon Jul 22 2024 07:48:02 GMT-0900 (Hawaii-Aleutian Daylight Time)