Created
December 16, 2023 12:31
-
-
Save allfinlir/8dbaeb881d73523593a513fd5690d6a4 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 | |
import Foundation | |
struct MasonryLayoutRows: View { | |
@State private var rows = 3 | |
@State private var views = (0..<20).map { _ in | |
CGSize(width: .random(in: 10...500), height: .random(in: 10...500))} | |
var body: some View { | |
ScrollView(.horizontal) { | |
LazyHStack { | |
MasontyLayoutRowsExample(rows: rows) { | |
ForEach(0..<20) { i in | |
APlaceHolderView(size: views[i]) | |
} | |
} | |
.padding(.vertical, 5) | |
} | |
} | |
.safeAreaInset(edge: .bottom) { | |
Stepper("Rows: \(rows)", value: $rows.animation(), in: 1...5) | |
.padding() | |
.background(.regularMaterial) | |
} | |
} | |
} | |
struct APlaceHolderView: View { | |
let color: Color = [.blue, .cyan, .green, .orange, .red, .indigo, .mint, .pink, .purple].randomElement()! | |
let size: CGSize | |
var body: some View { | |
ZStack { | |
RoundedRectangle(cornerRadius: 10) | |
.fill(color) | |
Text("\(Int(size.width))x\(Int(size.height))") | |
.foregroundStyle(.white) | |
.font(.headline) | |
} | |
.aspectRatio(size, contentMode: .fill) | |
} | |
} | |
struct MasontyLayoutRowsExample: Layout { | |
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) -> CGSize { | |
let height = proposal.replacingUnspecifiedDimensions().height | |
let viewFrames = frames(in: subviews, in: height) | |
let width = viewFrames.max { $0.maxX < $1.maxX } ?? .zero | |
return CGSize(width: width.maxX, height: height) | |
} | |
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) { | |
let viewFrames = frames(in: subviews, in: bounds.height) | |
for index in subviews.indices { | |
let frame = viewFrames[index] | |
let position = CGPoint(x: bounds.minX + frame.minX, y: bounds.minY + frame.minY) | |
subviews[index].place(at: position, anchor: .bottom, proposal: ProposedViewSize(frame.size)) | |
} | |
} | |
var rows: Int | |
var spacing: Double | |
init(rows: Int = 3, spacing: Double = 5) { | |
self.rows = max(1, rows) | |
self.spacing = spacing | |
} | |
func frames(in subviews: Subviews, in totalHeight: Double) -> [CGRect] { | |
let totalSpacing = spacing * Double(rows - 1) | |
let rowHeight = (totalHeight - totalSpacing) / Double(rows) | |
let rowHeightWithSpacing = rowHeight + spacing | |
let proposedSize = ProposedViewSize(width: nil, height: rowHeight) | |
var viewFrames = [CGRect]() | |
var rowWidths = Array(repeating: 0.0, count: rows) | |
for subview in subviews { | |
var selectedRow = 0 | |
var selectedWidth = Double.greatestFiniteMagnitude | |
for (rowIndex, width) in rowWidths.enumerated() { | |
if width < selectedWidth { | |
selectedRow = rowIndex | |
selectedWidth = width | |
} | |
} | |
let x = rowWidths[selectedRow] | |
let y = Double(selectedRow) * rowHeightWithSpacing | |
let size = subview.sizeThatFits(proposedSize) | |
let frame = CGRect(x: x, y: y, width: size.width, height: size.height) // jag tror att jag måste fixa in våra tag entries här? | |
rowWidths[selectedRow] += size.width + spacing | |
viewFrames.append(frame) | |
} | |
return viewFrames | |
} | |
} | |
#Preview { | |
MasonryLayoutRows() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment