Skip to content

Instantly share code, notes, and snippets.

@arashkashi
Last active July 6, 2021 17:16
Show Gist options
  • Save arashkashi/6e2bc7c30c9afa10ea9e970f6e6a3188 to your computer and use it in GitHub Desktop.
Save arashkashi/6e2bc7c30c9afa10ea9e970f6e6a3188 to your computer and use it in GitHub Desktop.
layout horizontally with new line instead of wrapping.
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 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]
var spacing: CGSize
var maxWidth: CGFloat = 0
@State var containerWidth: CGFloat = 0.0
@State var sizes: [CGSize] = []
var body: some View {
VStack {
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
let points = layout(sizes: self.sizes,
spacing: spacing,
containerWidth: self.containerWidth)
guard !points.isEmpty else { return 0 }
if index >= points.count { return 0 }
return -points[index].y
})
.alignmentGuide(.leading, computeValue: { dimension in
let points = layout(sizes: self.sizes,
spacing: spacing,
containerWidth: self.containerWidth)
guard !points.isEmpty else { return 0 }
if index >= points.count { return 0 }
return -points[index].x
})
}
}.onPreferenceChange(SizeArrayKey.self, perform: { value in
self.sizes = value
})
.frame(minWidth: 0, maxWidth: self.maxWidth)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment