🟠grid layout algorithm

⬆️ 需要: .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❓
Last updated
Was this helpful?