Skip to content

Instantly share code, notes, and snippets.

@alexdremov
Created August 11, 2022 21:23
Show Gist options
  • Save alexdremov/3fc5eebec4932a9550d7efc8a35f1f34 to your computer and use it in GitHub Desktop.
Save alexdremov/3fc5eebec4932a9550d7efc8a35f1f34 to your computer and use it in GitHub Desktop.
ColumnsLayout(columnsNumber: 2) {
VStack {
Text("That's one view")
Image(systemName: "tortoise.fill")
}
.padding()
.border(.red)
Text("That's the second view ")
.padding()
.border(.red)
Text("That's the third view with long lines that are warped automatically")
.fixedSize(horizontal: false, vertical: true)
.padding()
.border(.red)
}
.border(.blue)
.padding()
/**
Get array of heights for every row.
Just get max height on every row
*/
private func getRowHeights(subviews: Subviews, subviewProposal: ProposedViewSize) -> [CGFloat] {
var subviewProposalNoHLimit = subviewProposal
subviewProposalNoHLimit.height = .infinity
var rowHeights = [CGFloat]()
var index = 0
while index < subviews.count {
var rowMax: CGFloat = 0
for _ in 0..<columnsNumber where index < subviews.count {
let size = subviews[index].sizeThatFits(subviewProposalNoHLimit)
rowMax = max(rowMax, size.height)
index += 1
}
rowHeights.append(rowMax)
}
return rowHeights
}
/**
Calculates proposal for subview — one cell in table
*/
func getSubviewProposal(subviewsCount: Int, from globalProposal: ProposedViewSize) -> ProposedViewSize {
let rowHeight = max(ceil(Double(subviewsCount / columnsNumber)), 1)
return ProposedViewSize(
width: (globalProposal.width ?? 0)
/ CGFloat(columnsNumber),
height: (globalProposal.height ?? 0) / rowHeight
)
}
public func placeSubviews(
in bounds: CGRect,
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout ()
) {
var subviewProposal = getSubviewProposal(
subviewsCount: subviews.count,
from: proposal
)
let colRealWidth = subviewProposal.width ?? 0
let rowHeights = getRowHeights(subviews: subviews, subviewProposal: subviewProposal)
var curPos: CGFloat = bounds.minX
var curHeight: CGFloat = bounds.minY
var rowIndex = 0
for (index, subview) in subviews.enumerated() {
subviewProposal.height = rowHeights[rowIndex]
let size = subview.dimensions(in: subviewProposal)
subview.place(
at: CGPoint(x: curPos, y: curHeight),
anchor: .topLeading,
proposal: subviewProposal
)
if index % columnsNumber == columnsNumber - 1 {
curPos = bounds.minX
curHeight += rowHeights[rowIndex]
rowIndex += 1
} else {
curPos += colRealWidth
}
}
}
public func sizeThatFits(
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout ()
) -> CGSize {
let subviewProposal = getSubviewProposal(
subviewsCount: subviews.count,
from: proposal
)
let rowHeights = getRowHeights(
subviews: subviews,
globalProposal: proposal
)
let resultWidth = proposal.width ??
((subviewProposal.width ?? 0) * CGFloat(columnsNumber))
return CGSize(
width: resultWidth,
height: rowHeights.reduce(0, +)
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment