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
).
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)
}
}
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
}
}
}
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)
.