AdaptiveGridLayout

main

Adaptive vertical grid layout for SwiftUI
cenkbilgen/AdaptiveGridLayout

AdaptiveGridLayout

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
                Rectangle()
                    .fill(items[index].color)
                    .frame(width: items[index].width, height: itemHeight)
                    .overlay { Text(index, format: .number) }
            }

        }
        .border(.blue)
        .containerRelativeFrame(.vertical)
    }

}
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(fruit.name))
            }
        }
    }
    
    struct TagView: View {
        let word: LocalizedStringKey
        var body: some View {
            Text(word)
                .font(.body.weight(.semibold).monospaced())
                .padding(.vertical, 4)
                .padding(.horizontal)
                .background(Color.red, in: Capsule(style: .circular))
                .foregroundStyle(.background)
        }
    }
}

// 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

Animation

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: fruit.id, in: namespace) to each element then .animation(.spring, value: model.fruits).

looping_output

Description

  • Swift Tools 5.9.0
View More Packages from this Author

Dependencies

  • None
Last updated: Mon May 13 2024 05:53:57 GMT-0900 (Hawaii-Aleutian Daylight Time)