๐ grid layout algorithm
Last updated
Last updated
โฌ๏ธ ้่ฆ๏ผ .dimension(), VGridForEach
struct GridLayoutView: View {
/// one .adaptive column
let oneAdaptive = [
GridItem(.adaptive(minimum: 50))
]
/// two .flexible columns
let twoFlexibles = [
GridItem(.flexible(minimum: 100)),
GridItem(.flexible(minimum: 180)),
]
/// three mixed columns
let threeMixed = [
GridItem(.flexible(minimum: 100)),
GridItem(.adaptive(minimum: 40)),
GridItem(.flexible(minimum: 180)),
]
/// view body
var body: some View {
VStack(spacing: 50) {
grid(items: 20, width: 400, columns: oneAdaptive, color: .purple)
grid(items: 4, width: 300, columns: twoFlexibles, color: .pink)
grid(items: 10, width: 300, columns: threeMixed, leading: true)
}
}
/// grid()
@ViewBuilder func grid(
items count: Int,
width : CGFloat, // proposed width
columns : [GridItem],
leading : Bool = false, // height dimension on leading side
color : Color = pacificBlue // item color
) -> some View {
VGridForEach(0..<count, columns: columns){ i in
color.frame(height: 50).dimension(.vcenter, arrow: darkCerulean)
}.framedDimension(width: width, border: .yellow, leading: leading)
}
}
// helper extension
extension View {
@ViewBuilder func framedDimension(
width : CGFloat, // view width
border color: Color = .yellow, // view's border color
leading : Bool = false // height dimensions on leading side
) -> some View {
self
.frame(width: width)
.border(color)
// ๐ view.dimension()
.dimension(leading ? .bottomLeading : .bottomTrailing)
}
}
grid's layout algorithm has two passes:
layout : starts with the grid width (proposed width).
rendering : starts with the calculated overall width from the layout pass.
// โญ r: remaining width (r0 = initial value = grid width)
// โญ n: # of remaining cols (initial = all - fixed cols)
// โญโญโโโโโ F โโโโโฎ โญโโโโโโโโโ S โโโโโโโโโฎ
r -= (fixed widths) + (spacing between cols)
// โญ iterate over the remaining cols in order
(remaining cols).forEach { col in
let w = clamp( r / n ) // clamped column width โญ
// adaptive col is not clampedโ
r -= w // update remaining width
n -= 1 // update # of remaining cols
}
// โญ calculated overall width:
W = F + S + ฮฃw
// ----------------------
// rendering pass
// ----------------------
// if W == r0, there's no need to run 2nd pass,
// the result will still be the same.
guard W != r0 else { return }
// โญ grid's frame will be updated to fit the new width
// frame: same center, new origin (top left corner)
// โญ start with the overall width
r = W
repeat layoutAlgorithmAgain()
// โญ Caution:
// grid's frame might be updated agian,
// which may cause the grid to shift
// from its original center.
objc โฉ SwiftUIโs Grid Views โญ๏ธโญ๏ธ - how flexible/adaptive columns behave.
adaptive column - grid layout example
uses .dimension() to show width/height dimensions.
uses VGridForEach to combine LazyVGrid + ForEach.
ๅ๏ผwhat will the algorithm do to the adaptive columnsโto clamp or notโ