# MonthView

## 程式碼

![](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5-JmwCZMKh_d7RfBaN%2F-MJeLqz-GOzElf8Ev3cZ%2F-MJeN4wRmn6JyGcZLhTQ%2FMonthView.gif?alt=media\&token=49f8238a-58e5-4865-a420-44fe060e4347)

### 使用 Veiw Preference

* 利用自製的 View extension [.**registerFrame**(to: key, in: space)](https://lochiwei.gitbook.io/ios/swiftui/data-flow/preferences/examples/same-width/view+pref) 來收集所有 **MonthView** 的 frame，然後再由 parent view 的 .**onPreferenceChange**() 來更新 **YearView** 的 @State 變數 `frames`。

{% tabs %}
{% tab title="💈 範例 " %}

```swift
import SwiftUI
import PlaygroundSupport

// 月份名稱
let monthNames = [
    "ㄧ月", "二月", "三月","四月", "五月", "六月", 
    "七月", "八月", "九月","十月", "十一月", "十二月"
]

// ⭐️ 收集與處理所有的 MonthView's frame
typealias Frames = AllValues<CGRect>    // 📦 AllValues<T>

// live view
struct ContentView: View {
    let r: CGFloat = 1
    // view body
    var body: some View {
        YearView()   // 🌅 YearView
            .shadow(color: .black, radius: r, x: r, y: r)
    }
}

PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="🌅 YearView" %}

```swift
// 🌅 YearView
struct YearView: View {
    
    // ⭐️ 當前月份
    @State private var currentIndex = 0
    // ⭐️ 準備接收來自 Frames 的資料，用於更新 MonthView
    @State private var frames = [CGRect](repeating: .zero, count: 12)
    
    // ⭐️ 當前月份的「圓角框線」
    var roundedBorder: some View {
        
        // ⭐️ 當前月份的 frame
        let rect = frames[currentIndex]
        let rounded = RoundedRectangle(cornerRadius: 4)
        
        return rounded
            .fill(Color.yellow.opacity(0.2))
            .overlay(rounded.stroke(Color.pink, lineWidth: 3))
            // ⭐️ 設定尺寸
            .frame(rect.size)    // 🌀View + frame
            // ⭐️ 設定位移 (注意：要配合 ZStack 的「對齊方式：.topLeading」才有效)
            .offset(x: rect.minX, y: rect.minY)
            .animation(.default)
    }
    
    // view body
    var body: some View {
        // ⭐️ 對齊「左上角」：
        ZStack(alignment: .topLeading) {
            // 📦 StackForEach
            HStackForEach(0..<4, spacing: 16) { j in     // 一年四季
                VStackForEach(0..<3, spacing: 8) { i in  // 每季三月
                    // 🌅 MonthView
                    MonthView(index: i + 3*j, current: self.$currentIndex)
                        .border(Color.gray.opacity(0.1))
                }
            }// HStackForEach (container)
                // ⭐️ 在這裡定義座標系統 "container"
                .coordinateSpace(name: "container")
            
            // ⭐️ current month view's border
            roundedBorder
            
        }// ZStack
            .padding()
            .background(Color.gray)
            .animation(.spring())
            // ⭐️ 根據收集來的 Frames，更新 self.frames
            .onPreferenceChange(Frames.self) { self.frames = $0 }
    }// body
}
```

{% endtab %}

{% tab title="🌅 MonthView" %}

```swift
// 🌅 MonthView
struct MonthView: View {
    
    @Binding var currentIndex: Int  // 接收與更新當前月份 (⭐️ @Binding)
    let index                : Int  // 自己的月份
    
    // init
    init(index: Int, current: Binding<Int>) {
        self._currentIndex = current
        self.index         = index
    }
    
    // view body
    var body: some View {
        Text(monthNames[index])
            .padding(.horizontal, 12)
            .padding(.vertical, 4)
            // ⭐️ 將自己的 frame 加到 Frames
            .appendFrame(to: Frames.self, in: .named("container"))  // 🌀View + ref
            .animation(.default)
            // ⭐️ 當按到時，主動更新 currentIndex
            .onTapGesture { self.currentIndex = self.index }
    }
}
```

{% endtab %}

{% tab title="💈 全 " %}

```swift
// 初版： 2020.10.15

import SwiftUI
import PlaygroundSupport

// 月份名稱
let monthNames = [
    "ㄧ月", "二月", "三月","四月", "五月", "六月", 
    "七月", "八月", "九月","十月", "十一月", "十二月"
]

// ⭐️ 收集與處理所有的 MonthView's frame
typealias Frames = AllValues<CGRect>

// 🌅 MonthView
struct MonthView: View {
    
    @Binding var currentIndex: Int  // 接收與更新當前月份 (⭐️ @Binding)
    let index                : Int  // 自己的月份
    
    // init
    init(index: Int, current: Binding<Int>) {
        self._currentIndex = current
        self.index         = index
    }
    
    // view body
    var body: some View {
        Text(monthNames[index])
            .padding(.horizontal, 12)
            .padding(.vertical, 4)
            // ⭐️ 將自己的 frame 加到 Frames
            .appendFrame(to: Frames.self, in: .named("container"))  // 🌀View + ref
            .animation(.default)
            // ⭐️ 當按到時，主動更新 currentIndex
            .onTapGesture { self.currentIndex = self.index }
    }
}


// 🌅 YearView
struct YearView: View {
    
    // ⭐️ 當前月份
    @State private var currentIndex = 0
    // ⭐️ 準備接收來自 Frames 的資料，用於更新 MonthView
    @State private var frames = [CGRect](repeating: .zero, count: 12)
    
    // ⭐️ 當前月份的「圓角框線」
    var roundedBorder: some View {
        
        // ⭐️ 當前月份的 frame
        let rect = frames[currentIndex]
        let rounded = RoundedRectangle(cornerRadius: 4)
        
        return rounded
            .fill(Color.yellow.opacity(0.2))
            .overlay(rounded.stroke(Color.pink, lineWidth: 3))
            // ⭐️ 設定尺寸
            .frame(rect.size)    // 🌀View + frame
            // ⭐️ 設定位移 (注意：要配合 ZStack 的「對齊方式：.topLeading」才有效)
            .offset(x: rect.minX, y: rect.minY)
            .animation(.default)
    }
    
    // view body
    var body: some View {
        // ⭐️ 對齊「左上角」：
        ZStack(alignment: .topLeading) {
            // 📦 StackForEach
            HStackForEach(0..<4, spacing: 16) { j in     // 一年四季
                VStackForEach(0..<3, spacing: 8) { i in  // 每季三月
                    // 🌅 MonthView
                    MonthView(index: i + 3*j, current: self.$currentIndex)
                        .border(Color.gray.opacity(0.1))
                }
            }// HStackForEach (container)
                // ⭐️ 在這裡定義座標系統 "container"
                .coordinateSpace(name: "container")
            
            // ⭐️ current month view's border
            roundedBorder
            
        }// ZStack
            .padding()
            .background(Color.gray)
            .animation(.spring())
            // ⭐️ 根據收集來的 Frames，更新 self.frames
            .onPreferenceChange(Frames.self) { self.frames = $0 }
    }// body
}

struct ContentView: View {
    let r: CGFloat = 1
    // view body
    var body: some View {
        YearView()   // 🌅 YearView
            .shadow(color: .black, radius: r, x: r, y: r)
    }
}

// live view
PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="👥 相關 " %}

* [💈  動態底線](https://lochiwei.gitbook.io/ios/swiftui/view/state/value/state/underline)
* [🅿️ PreferenceKey](https://lochiwei.gitbook.io/ios/swiftui/data-flow/preferences/preferencekey) (`Frames = AllValues<CGRect>`)
* [allvalues-less-than-t-greater-than](https://lochiwei.gitbook.io/ios/swiftui/data-flow/preferences/preferencekey/allvalues-less-than-t-greater-than "mention")
* [🌀 View](https://lochiwei.gitbook.io/ios/swiftui/view/view/view) + [.frame(size)](https://lochiwei.gitbook.io/ios/swiftui/view/layout/frame/ext)
* [🌀View + pref](https://lochiwei.gitbook.io/ios/swiftui/data-flow/preferences/examples/same-width/view+pref) (`appendFrame`)&#x20;
* [📦 StackForEach](https://lochiwei.gitbook.io/ios/swiftui/view/layout/stacks/stackforeach)
  {% endtab %}

{% tab title="📘 參考 " %}

* [x] [Inspecting the View Tree (Part 1): PreferenceKey](https://swiftui-lab.com/communicating-with-the-view-tree-part-1/) - The SwiftUI Lab
  {% endtab %}
  {% endtabs %}

### 使用 Anchor

* 省掉用 geo.frame(in: space) 換算座標。
* 省掉用 .coordinateSpace(name:) 定義座標系統。
* 省掉用 @State 變數來管理畫面更新。

{% tabs %}
{% tab title="💈 範例 " %}

```swift
import SwiftUI
import PlaygroundSupport

// ⭐️ 收集與處理所有的 MonthView's frame
typealias FrameAnchors = AllValues<Anchor<CGRect>>

// live view
struct ContentView: View {
    let r: CGFloat = 1
    // view body
    var body: some View {
        YearView()   // 🌅 YearView
            .shadow(color: .black, radius: r, x: r, y: r)
    }
}

PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="🌅 YearView" %}

```swift
// 🌅 YearView
struct YearView: View {
    
    // ⭐️ 當前月份
    @State private var currentIndex = 0
    
    // ------------------------------------------------------
    // ⭐️ 由於使用 Anchor<CGRect> 來管理 MonthView 的 frames，
    //    所以 YearView 並不需要多一個 @State 變數來負責更新的工作。
    // ------------------------------------------------------
    
    // ⭐️ 當前月份的「圓角框線」
    func roundedBorder(anchors: FrameAnchors.Value) -> some View {
        
        // ⭐️ 當前月份的 anchor
        let anchor = anchors[currentIndex]                  // Anchor<CGRect>
        let rounded = RoundedRectangle(cornerRadius: 4)
        
        // ⭐️ 利用此輔助函數做計算，並傳回「圓角框線」給 GeometryReader 用
        func makeView(with geo: GeometryProxy) -> some View {
            
            // -------------------------------------------
            // ⭐️ 將 anchor 轉為這個座標系統的 frame (CGRect) 
            let rect = geo[anchor]
            // -------------------------------------------
            
            // 傳回「圓角框線」
            return rounded
                .fill(Color.yellow.opacity(0.2))
                .overlay(rounded.stroke(Color.pink, lineWidth: 3))
                // ⭐️ 設定尺寸
                .frame(rect.size)    // 🌀View + frame
                // ⭐️ 設定位移 (注意：要配合 ZStack 的「對齊方式：.topLeading」才有效)
                .offset(x: rect.minX, y: rect.minY)
                .animation(.default)
        }
        
        return GeometryReader { makeView(with: $0) }
    }
    
    // view body
    var body: some View {
        
        // 📦 StackForEach
        HStackForEach(0..<4, spacing: 16) { j in     // 一年四季
            VStackForEach(0..<3, spacing: 8) { i in  // 每季三月
                // 🌅 MonthView
                MonthView(index: i + 3*j, current: self.$currentIndex)
                    .border(Color.gray.opacity(0.1))
            }
        }// HStackForEach (container)
            // ------------------------------------------------------
            // ⭐️ 利用 FrameAnchors 的資料，在這個座標系統中畫「圓角框線」
            .overlayPreferenceValue(FrameAnchors.self) { (anchors) in
                self.roundedBorder(anchors: anchors)   }
            // ------------------------------------------------------
            .padding()
            .background(Color.gray)
            .animation(.spring())
            
    }// body
}
```

{% endtab %}

{% tab title="🌅 MonthView" %}

```swift
// 🌅 MonthView
struct MonthView: View {
    
    @Binding var currentIndex: Int  // 接收與更新當前月份 (⭐️ @Binding)
    let index                : Int  // 自己的月份
    
    // 月份名稱
    let monthNames = [
        "ㄧ月", "二月", "三月","四月", "五月", "六月", 
        "七月", "八月", "九月","十月", "十一月", "十二月"
    ]
    
    // init
    init(index: Int, current: Binding<Int>) {
        self._currentIndex = current
        self.index         = index
    }
    
    // view body
    var body: some View {
        Text(monthNames[index])
            .padding(.horizontal, 12)
            .padding(.vertical, 4)
            
            // ---------------------------------------------------------------
            // ⭐️ 將自己的 frame 加到 FrameAnchors
            .anchorPreference(key: FrameAnchors.self, value: .bounds) { [$0] }
            // ---------------------------------------------------------------
            
            .animation(.default)
            // ⭐️ 當按到時，主動更新 currentIndex
            .onTapGesture { self.currentIndex = self.index }
    }
}
```

{% endtab %}

{% tab title="💈 全 " %}

```swift
import SwiftUI
import PlaygroundSupport

// ⭐️ 收集與處理所有的 MonthView's frame
typealias FrameAnchors = AllValues<Anchor<CGRect>>

// 🌅 MonthView
struct MonthView: View {
    
    @Binding var currentIndex: Int  // 接收與更新當前月份 (⭐️ @Binding)
    let index                : Int  // 自己的月份
    
    // 月份名稱
    let monthNames = [
        "ㄧ月", "二月", "三月","四月", "五月", "六月", 
        "七月", "八月", "九月","十月", "十一月", "十二月"
    ]
    
    // init
    init(index: Int, current: Binding<Int>) {
        self._currentIndex = current
        self.index         = index
    }
    
    // view body
    var body: some View {
        Text(monthNames[index])
            .padding(.horizontal, 12)
            .padding(.vertical, 4)
            // ---------------------------------------------------------------
            // ⭐️ 將自己的 frame 加到 FrameAnchors
            .anchorPreference(key: FrameAnchors.self, value: .bounds) { [$0] }
            // ---------------------------------------------------------------
            .animation(.default)
            // ⭐️ 當按到時，主動更新 currentIndex
            .onTapGesture { self.currentIndex = self.index }
    }
}


// 🌅 YearView
struct YearView: View {
    
    // ⭐️ 當前月份
    @State private var currentIndex = 0
    
    // ------------------------------------------------------
    // ⭐️ 由於使用 Anchor<CGRect> 來管理 MonthView 的 frames，
    //    所以 YearView 並不需要多一個 @State 變數來負責更新的工作。
    // ------------------------------------------------------
    
    // ⭐️ 當前月份的「圓角框線」
    func roundedBorder(anchors: FrameAnchors.Value) -> some View {
        
        // ⭐️ 當前月份的 anchor
        let anchor = anchors[currentIndex]                  // Anchor<CGRect>
        let rounded = RoundedRectangle(cornerRadius: 4)
        
        // ⭐️ 利用此輔助函數做計算，並傳回「圓角框線」給 GeometryReader 用
        func makeView(with geo: GeometryProxy) -> some View {
            
            // ⭐️ 將 anchor 轉為這個座標系統的 frame (CGRect) 
            let rect = geo[anchor]
            
            // 傳回「圓角框線」
            return rounded
                .fill(Color.yellow.opacity(0.2))
                .overlay(rounded.stroke(Color.pink, lineWidth: 3))
                // ⭐️ 設定尺寸
                .frame(rect.size)    // 🌀View + frame
                // ⭐️ 設定位移 (注意：要配合 ZStack 的「對齊方式：.topLeading」才有效)
                .offset(x: rect.minX, y: rect.minY)
                .animation(.default)
        }
        
        return GeometryReader { makeView(with: $0) }
    }
    
    // view body
    var body: some View {
        
        // 📦 StackForEach
        HStackForEach(0..<4, spacing: 16) { j in     // 一年四季
            VStackForEach(0..<3, spacing: 8) { i in  // 每季三月
                // 🌅 MonthView
                MonthView(index: i + 3*j, current: self.$currentIndex)
                    .border(Color.gray.opacity(0.1))
            }
        }// HStackForEach (container)
            // ------------------------------------------------------
            // ⭐️ 利用 FrameAnchors 的資料，在這個座標系統中畫「圓角框線」
            .overlayPreferenceValue(FrameAnchors.self) { (anchors) in
                self.roundedBorder(anchors: anchors)   }
            // ------------------------------------------------------
            .padding()
            .background(Color.gray)
            .animation(.spring())
            
    }// body
}

struct ContentView: View {
    let r: CGFloat = 1
    // view body
    var body: some View {
        YearView()   // 🌅 YearView
            .shadow(color: .black, radius: r, x: r, y: r)
    }
}

// live view
PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="👥 相關 " %}

* [📦  PreferenceKeys](https://lochiwei.gitbook.io/ios/master/todo/exercises/broken-reference)
* [🌀 View](https://lochiwei.gitbook.io/ios/swiftui/view/view/view) [+ preference](https://lochiwei.gitbook.io/ios/swiftui/data-flow/preferences/examples/same-width/view+pref)
  {% endtab %}
  {% endtabs %}

### 使用 Anchor + View extension

* 使用自製的 🌀[View + preference](https://lochiwei.gitbook.io/ios/swiftui/data-flow/preferences/examples/same-width/view+pref)，可以讓語法更簡潔易懂。

{% tabs %}
{% tab title="💈 範例 " %}

```swift
import SwiftUI
import PlaygroundSupport

// ⭐️ 收集與處理所有的 MonthView's frame
typealias FrameAnchors = AllValues<Anchor<CGRect>>

// live view
struct ContentView: View {
    let r: CGFloat = 1
    // view body
    var body: some View {
        YearView()   // 🌅 YearView
            .shadow(color: .black, radius: r, x: r, y: r)
    }
}

PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="🌅  YearView" %}

```swift
// 🌅 YearView
struct YearView: View {
    
    // ⭐️ 當前月份
    @State private var currentIndex = 0
    
    // ------------------------------------------------------
    // ⭐️ 由於使用 Anchor<CGRect> 來管理 MonthView 的 frames，
    //    所以 YearView 並不需要多一個 @State 變數來負責更新的工作。
    // ------------------------------------------------------
    
    // ⭐️ 當前月份的「圓角框線」
    func roundedBorder(anchors: FrameAnchors.Value) -> some View {
        
        // ⭐️ 利用此輔助函數做計算，並傳回「圓角框線」給 GeometryReader 用
        func makeView(with geo: GeometryProxy) -> some View {
            
            // ------------------------------------------
            // ⭐️ 將 anchor 轉為這個座標系統的 frame (CGRect) 
            let anchor = anchors[currentIndex]     // Anchor<CGRect>
            let rect   = geo[anchor]               // CGRect
            // ------------------------------------------
            
            let rounded = RoundedRectangle(cornerRadius: 4)
            
            // 傳回「圓角框線」
            return rounded
                .fill(Color.yellow.opacity(0.2))
                .overlay(rounded.stroke(Color.pink, lineWidth: 3))
                // ⭐️ 設定尺寸
                .frame(rect.size)    // 🌀View + frame
                .offset(x: rect.minX, y: rect.minY)
                .animation(.default)
        }
        
        return GeometryReader { makeView(with: $0) }
    }
    
    // view body
    var body: some View {
        
        // 📦 StackForEach
        HStackForEach(0..<4, spacing: 16) { j in     // 一年四季
            VStackForEach(0..<3, spacing: 8) { i in  // 每季三月
                // 🌅 MonthView
                MonthView(index: i + 3*j, current: self.$currentIndex)
                    .border(Color.gray.opacity(0.1))
            }
        }// HStackForEach (container)
        
            // --------------------------------------------------
            // ⭐️ 利用 FrameAnchors 的資料，在這個座標系統中畫「圓角框線」
            .overlay(with: FrameAnchors.self) { // 🌀View + preference
                self.roundedBorder(anchors: $0) }
            // --------------------------------------------------
            
            .padding()
            .background(Color.gray)
            .animation(.spring())
            
    }// body
}
```

{% endtab %}

{% tab title="🌅  MonthView" %}

```swift
// 🌅 MonthView
struct MonthView: View {
    
    @Binding var currentIndex: Int  // 接收與更新當前月份 (⭐️ @Binding)
    let index                : Int  // 自己的月份
    
    // 月份名稱
    let monthNames = [
        "ㄧ月", "二月", "三月","四月", "五月", "六月", 
        "七月", "八月", "九月","十月", "十一月", "十二月"
    ]
    
    // init
    init(index: Int, current: Binding<Int>) {
        self._currentIndex = current
        self.index         = index
    }
    
    // view body
    var body: some View {
        Text(monthNames[index])
            .padding(.horizontal, 12)
            .padding(.vertical, 4)
            // ---------------------------------
            // ⭐️ 將自己的 frame 加到 FrameAnchors
            .register(to: FrameAnchors.self)  // 🌀View + preference
            // ---------------------------------
            .animation(.default)
            // ⭐️ 當按到時，主動更新 currentIndex
            .onTapGesture { self.currentIndex = self.index }
    }
}
```

{% endtab %}

{% tab title="💈 全 " %}

```
/*
 Inspecting the View Tree – Part 2: AnchorPreferences
 https://swiftui-lab.com/communicating-with-the-view-tree-part-2/
 
 When using the Anchor<Value> as an index to the GeometryProxy, you get the represented CGRect or CGPoint value. And as a plus, you get it already translated to the coordinate space of the GeometryReader view.
 
 */

import SwiftUI
import PlaygroundSupport

// ⭐️ 收集與處理所有的 MonthView's frame
typealias FrameAnchors = AllValues<Anchor<CGRect>>

// 🌅 MonthView
struct MonthView: View {
    
    @Binding var currentIndex: Int  // 接收與更新當前月份 (⭐️ @Binding)
    let index                : Int  // 自己的月份
    
    // 月份名稱
    let monthNames = [
        "ㄧ月", "二月", "三月","四月", "五月", "六月", 
        "七月", "八月", "九月","十月", "十一月", "十二月"
    ]
    
    // init
    init(index: Int, current: Binding<Int>) {
        self._currentIndex = current
        self.index         = index
    }
    
    // view body
    var body: some View {
        Text(monthNames[index])
            .padding(.horizontal, 12)
            .padding(.vertical, 4)
            // ---------------------------------------------------------------
            // ⭐️ 將自己的 frame 加到 FrameAnchors
//              .anchorPreference(key: FrameAnchors.self, value: .bounds) { [$0] }
            .register(to: FrameAnchors.self)
            // ---------------------------------------------------------------
            .animation(.default)
            // ⭐️ 當按到時，主動更新 currentIndex
            .onTapGesture { self.currentIndex = self.index }
    }
}


// 🌅 YearView
struct YearView: View {
    
    // ⭐️ 當前月份
    @State private var currentIndex = 0
    
    // ------------------------------------------------------
    // ⭐️ 由於使用 Anchor<CGRect> 來管理 MonthView 的 frames，
    //    所以 YearView 並不需要多一個 @State 變數來負責更新的工作。
    // ------------------------------------------------------
    
    // ⭐️ 當前月份的「圓角框線」
    func roundedBorder(anchors: FrameAnchors.Value) -> some View {
        
        // ⭐️ 利用此輔助函數做計算，並傳回「圓角框線」給 GeometryReader 用
        func makeView(with geo: GeometryProxy) -> some View {
            
            // ------------------------------------------
            // ⭐️ 將 anchor 轉為這個座標系統的 frame (CGRect) 
            let anchor = anchors[currentIndex]             // Anchor<CGRect>
            let rect   = geo[anchor]                       // CGRect
            // ------------------------------------------
            
            let rounded = RoundedRectangle(cornerRadius: 4)
            
            // 傳回「圓角框線」
            return rounded
                .fill(Color.yellow.opacity(0.2))
                .overlay(rounded.stroke(Color.pink, lineWidth: 3))
                // ⭐️ 設定尺寸
                .frame(rect.size)    // 🌀View + frame
                // ⭐️ 設定位移 (注意：要配合 ZStack 的「對齊方式：.topLeading」才有效)
                .offset(x: rect.minX, y: rect.minY)
                .animation(.default)
        }
        
        return GeometryReader { makeView(with: $0) }
    }
    
    // view body
    var body: some View {
        
        // 📦 StackForEach
        HStackForEach(0..<4, spacing: 16) { j in     // 一年四季
            VStackForEach(0..<3, spacing: 8) { i in  // 每季三月
                // 🌅 MonthView
                MonthView(index: i + 3*j, current: self.$currentIndex)
                    .border(Color.gray.opacity(0.1))
            }
        }// HStackForEach (container)
            // --------------------------------------------------
            // ⭐️ 利用 FrameAnchors 的資料，在這個座標系統中畫「圓角框線」
            .overlay(with: FrameAnchors.self) { 
                self.roundedBorder(anchors: $0) }
            // --------------------------------------------------
            .padding()
            .background(Color.gray)
            .animation(.spring())
            
    }// body
}

struct ContentView: View {
    let r: CGFloat = 1
    // view body
    var body: some View {
        YearView()   // 🌅 YearView
            .shadow(color: .black, radius: r, x: r, y: r)
    }
}

// live view
PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="👥  相關 " %}

* [🌀 View](https://lochiwei.gitbook.io/ios/swiftui/view/view/view) [+ preference](https://lochiwei.gitbook.io/ios/swiftui/data-flow/preferences/examples/same-width/view+pref)
* [📦 StackForEach](https://lochiwei.gitbook.io/ios/swiftui/view/layout/stacks/stackforeach)
* [🔰 Anchor Preferences](https://lochiwei.gitbook.io/ios/swiftui/data-flow/preferences/anchor-preferences)
  {% endtab %}
  {% endtabs %}
