Skip to content

Instantly share code, notes, and snippets.

@arashkashi
Created June 2, 2021 07:01
Show Gist options
  • Save arashkashi/8affbbd06ce3789d081e04fec2d04b72 to your computer and use it in GitHub Desktop.
Save arashkashi/8affbbd06ce3789d081e04fec2d04b72 to your computer and use it in GitHub Desktop.
layout of fixed size elements into a container (top leading)
//
// ContentView.swift
// ffghgf
//
// Created by Arash on 2021-06-02.
//
import SwiftUI
struct SizeArrayKey: PreferenceKey {
static var defaultValue: [CGSize] = []
static func reduce(value: inout Value, nextValue: () -> Value ) {
value.append(contentsOf: nextValue())
}
}
struct Item: Identifiable {
var id = UUID()
var value: String
}
struct ContentView: View {
@State var items: [Item] = (1...10).map { "item \($0)" + (Bool.random() ? "\n" : "" + String.init(repeating: "x", count: Int.random(in: 0...10)))}.map { Item(value: $0)}
var body: some View {
let cells = items.map {
IdentifiableText(text: $0.value)
}
FlowLayout(cells: cells)
}
}
struct IdentifiableText: View, Identifiable {
var id: String = UUID().uuidString
var text: String
var body: some View {
Text(text)
.padding()
.background(RoundedRectangle(cornerRadius: 5).fill(Color.blue))
}
}
func layout(sizes: [CGSize], spacing: CGSize, containerWidth: CGFloat) -> [CGPoint] {
var result = [CGPoint]()
var currentPoint = CGPoint.zero
var maxHeight: CGFloat = 0
for size in sizes {
if currentPoint.x + size.width > containerWidth {
currentPoint.x = 0
currentPoint.y += spacing.height + maxHeight
maxHeight = 0.0
}
if size.height > maxHeight { maxHeight = size.height }
result.append(currentPoint)
currentPoint.x += size.width + spacing.width
}
return result
}
struct FlowLayout<Cell: View & Identifiable>: View {
var cells: [Cell]
@State var sizes: [CGSize] = []
@State var containerWidth: CGFloat = 0
var body: some View {
VStack {
let points = layout(sizes: self.sizes,
spacing: .init(width: 5, height: 5),
containerWidth: self.containerWidth)
GeometryReader { proxy in
Color.clear.preference(key: SizeArrayKey.self, value: [proxy.size])
}.frame(height: 0)
.onPreferenceChange(SizeArrayKey.self, perform: { value in
self.containerWidth = value[0].width
})
ZStack(alignment: .topLeading) {
ForEach(Array(zip(cells, cells.indices)), id: \.0.id) { (item, index) in
item
.fixedSize()
.background(GeometryReader { proxy in
Color.clear.preference(key: SizeArrayKey.self, value: [proxy.size])
})
.alignmentGuide(.top, computeValue: { dimension in
guard !points.isEmpty else { return 0 }
return -points[index].y
})
.alignmentGuide(.leading, computeValue: { dimension in
guard !points.isEmpty else { return 0 }
return -points[index].x
})
}
}.onPreferenceChange(SizeArrayKey.self, perform: { value in
self.sizes = value
})
.frame(minWidth: 0, maxWidth: .infinity)
.border(Color.red)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment