Created
May 19, 2023 16:43
-
-
Save walkingbrad/7664117bff9d54f4805f8143d3741c17 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
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