# adaptive column

[SwiftUI](../../../..) ⟩ [Layout](../..) ⟩ [Grids](..) ⟩ adaptive column

{% tabs %}
{% tab title="⭐️ 重點" %}
{% hint style="success" %}
<mark style="color:purple;">**.adaptive(minimum:maximum)**</mark>:&#x20;

* a single [GridItem]() may represent <mark style="color:red;">**more than one column**</mark>.&#x20;
* SwiftUI will try to fit as many <mark style="color:red;">**minimum width**</mark> columns as possible, but once the number of columns is determined, these can grow up to the <mark style="color:red;">**maximum width**</mark> provided, so it fills the full space.&#x20;
* The method to determine how much space will be given to this GridItem is the same used for the [flexible](flexible-column) case (<mark style="color:red;">**without the clamping**</mark>).
{% endhint %}

{% hint style="danger" %}
the <mark style="color:purple;">**minimum**</mark> property of an <mark style="color:red;">**adaptive column**</mark> is **only** used to **compute** the <mark style="color:orange;">**number of items**</mark> inside that column, it's <mark style="color:red;">**NOT**</mark> the <mark style="color:orange;">**minimum width**</mark> of that column❗️👉 [objc](https://www.objc.io/blog/2020/11/23/grid-layout/)
{% endhint %}
{% endtab %}

{% tab title="💈例一" %}
{% embed url="https://youtu.be/Sn7CMCrjP6I" %}

{% hint style="warning" %}
本範例雖然只有指定<mark style="color:red;">**兩個**</mark> [**GridItem**]()：

```swift
let columns = [
    GridItem(.fixed(70)),                // ⭐ fixed column
    GridItem(.adaptive(minimum: 40))     // ⭐ adaptive column
]
```

但結果卻出現<mark style="color:red;">**三個欄位**</mark>，這主要是因為其中一個是 <mark style="color:red;">**adaptive column**</mark>，這種欄位可以一次<mark style="color:red;">**容納多個項目**</mark> (**cells**)。
{% endhint %}

![](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5-JmwCZMKh_d7RfBaN%2Fuploads%2FajclPSQgK8zuPulsD7MA%2Ffixed-adaptive.png?alt=media\&token=d7c8a58a-7bf5-4e28-85c2-7b523f8463ec)

⬆️ 需要： 👔 [ScrollVGridForEach](../vgridforeach)

```swift
// ------------------------
//     ⭐ Fixed Column
// ------------------------
//
//  always rendered at the specified width
//  (no matter how much space is available)

// ---------------------------
//     ⭐ Adaptive Column
// ---------------------------
//
// ⭐ tries to accommodate "as many items as possible"
// ⭐ the "minimum" property of an "adaptive column" is only used 
//    to compute how many items that column can accommodate, 
//    it's NOT the minimum width of that column❗️
//    (adaptive column's width could be SMALLER than this property)

// ⭐ 1.   (remaining width)
//       = (grid width) - (fixed widths) - (spaces between 2 columns)
//       = (200) - (70) - (8 * 1)
//       = (122)
//
// ⭐ 2. calculate (how many items) an adaptive column can accommodate:
//         (remaining width) - (first item) - (following items) > 0
//       given: 
//          minimum width = (40), spacing = (8) : ⭐ default
//          fist item = (40), following item = (8 + 40)
//
//       (122) - (40) - (48) * 1 =  34 > 0 ... ✅  (accommodate 2 items)
//       (122) - (40) - (48) * 2 = -14 < 0 ... ❌
//
// ⭐ 3.   (item width)
//       = [(remaining) - (spaces between 2 items)] / (2 items)
//       = [(122) - (8 * 1)] / 2
//       = 114 / 2
//       = 57  ............ (proposed size for each item) ⭐
// 
// ⭐ result:
//
// ╭──fixed──╮   ╭───── adaptive ──────╮ (one adaptive column)
// [   70    ][8][   57   ][8][   57   ]
//               ╰──item──╯   ╰──item──╯ (two grid items)

struct ContentView: View {
    
    let columns = [
        GridItem(.fixed(70)),                // ⭐ fixed column
        GridItem(.adaptive(minimum: 40))     // ⭐ adaptive column
    ]
    
    var body: some View {
        grid(columns)
    }
}

extension ContentView {
    
    /// a grid of swatches
    func grid(_ columns: [GridItem]) -> some View {
        // 👔 ScrollVGridForEach
        ScrollVGridForEach(0..<100, columns: columns){ 
            numberSwatch($0)
        }
        .frame(width: 200)        // grid width = scroll view width ⭐ 
        .border(Color.yellow)     // scroll view border (yellow)
        .padding()
        .border(Color.blue)       // padding border (blue)
        .frame(height: 300)
    }
    
    /// a color swatch with a number on it
    @ViewBuilder func numberSwatch(_ i: Int) -> some View {
        let color: Color = i % 3 == 0 ? .green : .pink
        color
            .opacity(0.3)
            .frame(height: 40)
            .overlay(Text("\(i)"))
    }
}
```
{% endtab %}

{% tab title="💈例二" %}
{% hint style="warning" %}
&#x20;雖然本範例的 <mark style="color:red;">**adaptive columen**</mark> 有 <mark style="color:purple;">**minimum**</mark> 屬性為 <mark style="color:red;">**70**</mark>：

```swift
let columns = [
    GridItem(.flexible(minimum: 140)),  // ⭐ flexible
    GridItem(.adaptive(minimum: 70))    // ⭐ adaptive
]
```

但此屬性<mark style="color:red;">**並非**</mark>**&#x20;adaptive column 的**<mark style="color:red;">**最小寬度**</mark>，而是用於<mark style="color:orange;">**計算能容納多少項目**</mark> (**cells**) 時使用。
{% endhint %}

![](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5-JmwCZMKh_d7RfBaN%2Fuploads%2FYUViuwQuUyC3J1dmSQkB%2Fflex-adapt.png?alt=media\&token=953015ba-837d-40e2-a865-2f5aaa11e00e)

```swift
struct ContentView: View {
    
    let columns = [
        GridItem(.flexible(minimum: 140)),  // ⭐ flexible
        GridItem(.adaptive(minimum: 70))    // ⭐ adaptive
    ]
    
    var body: some View {
        grid(columns)
    }
}
```

{% hint style="info" %}
欄位寬度的計算方式：👉 [layout-algorithm](../layout-algorithm "mention")
{% endhint %}

```swift
// r: remaining width
r = 200    // grid width
n = 2      // # of remaining cols

// r -= (fixed widths) + (spacing between cols)
r -= 8     // 192

// 1st col width
w1 = clamp(r/n)     // 192/2 = 96
   = 140            // clamped to [140, .inf)

// update r, n
r -= w1             // 192 - 140 = 52
n -= 1              // 1

// 2nd col width
w2 = clamp(r/n)      // 52/1 = 52
   = 52              // adaptive col is NOT clamped ⭐
```
{% endtab %}

{% tab title="📘 手冊" %}
* SwiftUI ⟩ Layout ⟩ [LazyVGrid](https://developer.apple.com/documentation/swiftui/lazyvgrid) ⟩ [.init(columns:alignment: ...)](https://developer.apple.com/documentation/swiftui/lazyvgrid/init\(columns:alignment:spacing:pinnedviews:content:\))
{% endtab %}

{% tab title="📗 參考" %}
* [x] objc ⟩ [SwiftUI’s Grid Views](https://www.objc.io/blog/2020/11/23/grid-layout/) ⭐️⭐️ - how **flexible**/**adaptive** columns behave. (💈例一)
{% endtab %}

{% tab title="👥 相關" %}
* [layout-algorithm](../layout-algorithm "mention")
* [flexible-column](flexible-column "mention")
{% endtab %}
{% endtabs %}
