Last active
July 6, 2021 17:16
-
-
Save arashkashi/6e2bc7c30c9afa10ea9e970f6e6a3188 to your computer and use it in GitHub Desktop.
layout horizontally with new line instead of wrapping.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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