SwiftUI 列表視圖 (list view) 和 UIKit 的 UITableView 很類似。在 SwiftUI 最初的版本中,Apple 的工程師已經將創建列表視圖的過程變得輕而易舉,我們不需要創建 prototype cell,也不需要委派 (delegate) 或 data source 的協定。我們只需要用幾行程式碼,就可以使用客製化單元格來建構一個列表視圖。在 iOS 14 中,Apple 繼續改善列表視圖,並添加了一些新功能。在本篇教學文章中,我們將會看看如何建構一個展開式列表視圖 (expandable list view) 或大綱視圖 (outline view),並探索 inset grouped 的列表樣式 (list style)。
建構展開式列表視圖
首先,讓我們看看本篇教學的成品。我十分喜歡 La Marzocco,所以我用了它網站的導覽選單 (navigation menu) 為例子。以下的列表視圖展示了選單的大綱,使用者可以點擊顯示按鈕來展開列表。
![](https://www.appcoda.com/wp-content/uploads/2020/06/swiftui-expandable-list-preview.gif)
當然,你也可以用自己的實作方法,來建構這個大綱視圖。但在最新版本的 SwiftUI 中,Apple 讓開發者可以更簡單地建構這種大綱視圖,並自動適應於 iOS、iPadOS、和 macOS 版本。
要讓列表視圖可以展開,你只需要如此建立一個數據模型 (data model)。
struct MenuItem: Identifiable {
var id = UUID()
var name: String
var image: String
var subMenuItems: [MenuItem]?
}
在上面的程式碼中,我們有一個用來建造選單物件的結構 (struct)。要創建一個巢狀列表 (nested list),關鍵就是要加入一個屬性,包含子級的可選陣列 (optional array of children) subMenuItems
。請注意,子級與其父級的型別 (type) 是一樣的。
對於頂層 (top level) 選單物件,我們可以如此創建一個 MenuItem
陣列:
// Main menu items
let sampleMenuItems = [ MenuItem(name: "Espresso Machines", image: "linea-mini", subMenuItems: espressoMachineMenuItems),
MenuItem(name: "Grinders", image: "swift-mini", subMenuItems: grinderMenuItems),
MenuItem(name: "Other Equipment", image: "espresso-ep", subMenuItems: otherMenuItems)
]
我們會針對每個選單物件,指定子選單 (sub-menu) 物件的陣列。如果沒有子選單物件,則可以省略 subMenuItems
參數,或傳遞 nil
值。我們可以這樣定義子選單物件:
// Sub-menu items for Espressco Machines
let espressoMachineMenuItems = [ MenuItem(name: "Leva", image: "leva-x", subMenuItems: [ MenuItem(name: "Leva X", image: "leva-x"), MenuItem(name: "Leva S", image: "leva-s") ]),
MenuItem(name: "Strada", image: "strada-ep", subMenuItems: [ MenuItem(name: "Strada EP", image: "strada-ep"), MenuItem(name: "Strada AV", image: "strada-av"), MenuItem(name: "Strada MP", image: "strada-mp"), MenuItem(name: "Strada EE", image: "strada-ee") ]),
MenuItem(name: "KB90", image: "kb90"),
MenuItem(name: "Linea", image: "linea-pb-x", subMenuItems: [ MenuItem(name: "Linea PB X", image: "linea-pb-x"), MenuItem(name: "Linea PB", image: "linea-pb"), MenuItem(name: "Linea Classic", image: "linea-classic") ]),
MenuItem(name: "GB5", image: "gb5"),
MenuItem(name: "Home", image: "gs3", subMenuItems: [ MenuItem(name: "GS3", image: "gs3"), MenuItem(name: "Linea Mini", image: "linea-mini") ])
]
// Sub-menu items for Grinder
let grinderMenuItems = [ MenuItem(name: "Swift", image: "swift"),
MenuItem(name: "Vulcano", image: "vulcano"),
MenuItem(name: "Swift Mini", image: "swift-mini"),
MenuItem(name: "Lux D", image: "lux-d")
]
// Sub-menu items for other equipment
let otherMenuItems = [ MenuItem(name: "Espresso AV", image: "espresso-av"),
MenuItem(name: "Espresso EP", image: "espresso-ep"),
MenuItem(name: "Pour Over", image: "pourover"),
MenuItem(name: "Steam", image: "steam")
]
準備好數據模型後,我們可以編寫程式碼以呈現列表視圖。List
視圖現在有了可選的 children
參數,所以如果有任何子物件,你可以提供其 key path \.subMenuItems
。然後,SwiftUI 將遞歸 (recursively) 查找子選單物件,並以大綱形式顯示。以下是範例程式碼:
List(sampleMenuItems, children: \.subMenuItems) { item in
HStack {
Image(item.image)
.resizable()
.scaledToFit()
.frame(width: 50, height: 50)
Text(item.name)
.font(.system(.title3, design: .rounded))
.bold()
}
}
在 List
視圖閉包中,我們會描述每一行的外觀。在範例程式碼中,我們使用了 HStack
佈局圖像和文本視圖。如果你已經在 ContentView
中正確地添加了程式碼的話,SwiftUI 應該會如此呈現大綱視圖:
![swiftui-expandable-list-iphone-demo](https://www.appcoda.com/wp-content/uploads/2020/06/swiftui-expandable-list-demo.png)
使用 Inset Grouped 列表樣式
在 iOS 13 中,Apple 為 UITableView
帶來了一種新樣式 ── Inset Grouped,這個樣式會讓每個 section 的內容被包覆在一個圓角區塊內。但是,這種樣式不適用於 SwiftUI 框架中的 List
視圖。在即將發佈的 iOS 14 版本,Apple 就將這個新樣式添加到了 SwiftUI 列表中。
要使用這個新列表樣式,我們可添加 listStyle
修飾符 (modifier) 到 List
視圖,並向它傳遞 InsetGroupedListStyle
的實例:
List {
...
}
.listStyle(InsetGroupedListStyle())
如果你正確地跟著步驟做,列表視圖應該會變成 inset grouped 樣式:
![swiftui-inset-grouped-style-list](https://www.appcoda.com/wp-content/uploads/2020/06/swiftui-expandable-list-inset-grouped.png)
總結
在本篇教學中,我們介紹了 SwiftUI 列表視圖的兩個新功能。如你在範例中可見,要建構一個大綱視圖或展開式列表視圖非常容易,你要做的就只是準備好一個正確的數據模型,列表視圖就會幫你完成剩下的工作,遍歷數據結構,並呈現大綱視圖。
除了這些新功能外,Apple 還在 iOS 14 中加入了另一種列表樣式 SidebarListStyle。而且,新的更新也提供了 OutlineGroup
和 DisclosureGroup
,讓我們可以進一步客製大綱視圖。我們將在另一篇教學文章中進一步研究這些功能,請密切關注我們的更新。
原文:Building an Expandable List View with Inset Grouped Style Using SwiftUI