✨MyGrid
⭐️ 注意:👔 MyGrid
<
Item, ItemView
>
是一個 generics & subtypes❗️
使用 generic type 的 nested type 要小心❗️
👔 MyGrid (line: 44):在 MyGrid 內部使用 Layout 基本上沒什麼問題
👁️ TestMyGrid (line: 52):在 MyGrid 外面使用 Layout 就要小心了,如果該行寫成下面這樣:
let layout = MyGrid.Layout( ... )
那就會出現下列問題:
Files
⬆️ 需要: arr.index(of:)
// 2022.02.15
import SwiftUI
/// 👔 MyGrid<Item, ItemView>
/// ```
/// MyGrid(items: items) { item in
/// /* build item view */
/// }
/// ```
struct MyGrid<Item: Identifiable, ItemView: View>: View {
// 1. 使用者要提供:
// • items : 排版的項目
// • cellAspectRatio: 希望的格子比例 (default = 1)
// • viewForItem : 繪製項目視圖的方法 (@ViewBuilder closure)
let items: [Item]
var cellAspectRatio: CGFloat = 1
@ViewBuilder let viewForItem: (Item) -> ItemView
// 2. 當 MyGrid 開始排版時:
public var body: some View {
// ⭐️ 2.1 MyGrid 會利用 GeometryReader 得到 proposed size,
GeometryReader { geo in
// ⭐️ 2.2 然後用 `self.itemViews(in: size)` 繪製所有項目的視圖。
self.itemViews(in: geo.size)
}
}
}
extension MyGrid {
// 2.2
@ViewBuilder func itemViews(in size: CGSize) -> some View {
// ⭐️ 2.2.1: 委託 MyGrid.Layout 計算「最佳的排版方式」(layout)。
//
// layout 擁有以下資訊:
// • .rows, .cols:「該切成幾行幾列」
// • .cellSize :「格子比例」
// • .centerOfCell(at: i):「第幾個項目應該排在哪個位置」
//
// 👔 MyGrid<Item, ItemView>.Layout
let layout = Layout(
cellAspectRatio, count: items.count, in: size
)
// ⭐️ ForEach
ForEach(items) { item in
// ⭐️ 2.2.2: 委託 `self.view(for: item, in: layout)` 繪製每個項目的視圖。
self.view(for: item, in: layout)
}
}
// 2.2.2
@ViewBuilder func view(for item: Item, in layout: Layout) -> some View
{
// ⭐️ 2.2.2.1: 計算項目編號,得知它是第幾個項目。
let i = index(of: item)
// ⭐️ 2.2.2.2: 計算項目的中心位置
let center = layout.centerOfCell(at: i) // 👔 MyGrid<Item, ItemView>.Layout
// ⭐️ 2.2.2.3: 繪製項目視圖,並放在該放的位置。
viewForItem(item)
.frame(layout.cellSize) // ⭐️ cell size
// -------------------------------------------
// ⚠️ 注意:
// 一旦加上 .position 之後,.frame 會變成整個
// GeometryReader 的大小,不再是單一卡片的大小。
// -------------------------------------------
.position(center) // ⭐️ put item view in position
}
/// 2.2.2.1: index of item
func index(of item: Item) -> Int {
items.index(of: item)! // 🌀Array + .index(of:)
} // where Element: Identifiable
}
Last updated
Was this helpful?