Skip to content

Instantly share code, notes, and snippets.

@walkingbrad
Created May 19, 2023 16:43
Show Gist options
  • Save walkingbrad/7664117bff9d54f4805f8143d3741c17 to your computer and use it in GitHub Desktop.
Save walkingbrad/7664117bff9d54f4805f8143d3741c17 to your computer and use it in GitHub Desktop.
import SwiftUI
@available(iOS 16, *)
public struct FlowLayout: Layout {
private let spacing: CGFloat
public init(spacing: CGFloat = 0) {
self.spacing = spacing
}
public func makeCache(subviews: Subviews) -> FlowLayoutCache {
FlowLayoutCache(
sizes: subviews.map { $0.sizeThatFits(.unspecified) }
)
}
public func placeSubviews(
in bounds: CGRect,
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout FlowLayoutCache
) {
let maxX = bounds.minX + (proposal.width ?? 0)
var lineX = bounds.minX
var lineY = bounds.minY
var lineHeight: CGFloat = 0
func moveToNextLine() {
lineY += lineHeight + spacing
lineHeight = 0
lineX = bounds.minX
}
func placeSubview(_ subview: LayoutSubviews.Element, size: CGSize) {
subview.place(
at: CGPoint(x: lineX, y: lineY),
proposal: ProposedViewSize(size)
)
lineHeight = max(lineHeight, size.height)
lineX += size.width + spacing
}
for index in subviews.indices {
let subview = subviews[index]
let size = cache.sizes[index]
if lineX + size.width > maxX {
moveToNextLine()
}
placeSubview(subview, size: size)
}
}
public func sizeThatFits(
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout FlowLayoutCache
) -> CGSize {
var totalHeight: CGFloat = 0
var totalWidth: CGFloat = 0
var lineWidth: CGFloat = 0
var lineHeight: CGFloat = 0
for size in cache.sizes {
if lineWidth + spacing + size.width > (proposal.width ?? 0) {
totalHeight += lineHeight + spacing
lineWidth = size.width
lineHeight = size.height
} else {
lineWidth += size.width + spacing
lineHeight = max(lineHeight, size.height)
}
totalWidth = max(totalWidth, lineWidth)
}
totalHeight += lineHeight
return .init(width: totalWidth, height: totalHeight)
}
public func spacing(subviews: Subviews, cache: inout FlowLayoutCache) -> ViewSpacing {
.zero
}
}
public struct FlowLayoutCache {
fileprivate let sizes: [CGSize]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment