本篇要來實踐 SwiftUI 列表搜尋功能,在 LeetCode 題目越來越多且越來越複雜的情況下,要一個一個找想看的題目變得越來越麻煩,此時就需要善用搜尋功能快速找到我們想看的題目。
首先 SwiftUI 很方便,提供我們加入搜尋的功能。
struct ContentView: View {
@State private var searchText = ""
var body: some View {
NavigationStack {
Text("Searching for \(searchText)")
.navigationTitle("Searchable Example")
}
.searchable(text: $searchText)
}
}
可以在 NavigationStack
建立 .searchable
記錄使用者輸入的搜尋關鍵字,然後就有搜尋文字。searchText
這個狀態變數就是儲存輸入的搜尋關鍵字。
如圖,SwiftUI 給我們基本的搜尋框可以搜尋,一點擊就會出現鍵盤提供輸入,右邊會有清除文字的 Icon 跟取消搜尋。
再來加入之前篇章做好的 LeetCode 題目列表,搭配搜尋框幫助我們快速找到題目。
struct ContentView: View {
@State private var searchText = ""
let leetCodeProblems = [
LeetCodeProblem(id: 1, title: "Two Sum"),
LeetCodeProblem(id: 2, title: "Add Two Numbers"),
LeetCodeProblem(id: 3, title: "Longest Substring Without Repeating Characters"),
]
var searchResults: [LeetCodeProblem] {
if searchText.isEmpty {
return leetCodeProblems
} else {
return leetCodeProblems.filter { $0.title.contains(searchText) }
}
}
var body: some View {
NavigationStack {
List(searchResults) { problem in
NavigationLink(destination: Text(problem.title)) {
Text(problem.title)
}
}
}
.searchable(text: $searchText)
}
}
把原本顯示的 leetCodeProblems
放在 List
改顯示搜尋過後過濾的 searchResults
,這樣就達到效果了!
那如果希望搜尋有提供建議,可以這麼設定。
.searchable(text: $searchText){
ForEach(searchResults, id: \.self) { result in
Text("你在尋找 \(result.title) 嗎?").searchCompletion(result.title)
}
}
不過要注意資料物件要多繼承 Hashable
,才可以塞在 .searchable
。
struct LeetCodeProblem: Identifiable, Hashable {
var id: Int
var title: String
}
當在搜尋時游標出現就會自動跳出搜尋提示。
想更看多進階教學,可以看以下網站,因為後續功能跟 LeetCode 題目列表需求不是太相關,故不贅述。
由於前面我們介紹了不少 LeetCode 演算法主題,需要讓 LeetCode 題目跟著主題分類,不再只是顯示亂七八糟的題目讓我們無所適從。
SwiftUI 提供了 Section
可以讓列表做一個主題是分類。
我們會看到很多教學使用 Section 都是直接寫死在程式碼上,定義出哪幾個資料是要同樣主題
List {
Section(header: Text("主題")) {
Text("第一行")
Text("第二行")
Text("第三行")
}
}
一個簡單的列表主題就會顯示,不過這樣還不夠,我們要能應用在 LeetCode 列表裡面。
資料格式需要改變變成字典 Dictionary,Key 為 LeetCode 演算法主題。
let data: [String: [LeetCodeProblem]] = [
"Array": [LeetCodeProblem(id: 1, title: "Two Sum"),
LeetCodeProblem(id: 2, title: "Best Time to Buy and Sell Stock"),
LeetCodeProblem(id: 3, title: "Contains Duplicate"),
LeetCodeProblem(id: 4, title: "Product of Array Except Self"),
LeetCodeProblem(id: 5, title: "Maximum Subarray"),
],
"Dynamic Programming": [LeetCodeProblem(id: 6, title: "Climbing Stairs"),
LeetCodeProblem(id: 7, title: "Coin Change"),
LeetCodeProblem(id: 8, title: "Longest Increasing Subsequence")],
"Tree": [LeetCodeProblem(id: 9, title: "Maximum Depth of Binary Tree"),
LeetCodeProblem(id: 10, title: "Same Tree"),]
]
再來搜尋後的結果資料結構邏輯也需要調整。
var searchResults: [String: [LeetCodeProblem]] {
if searchText.isEmpty {
return data
} else {
var filteredResults: [String: [LeetCodeProblem]] = [:]
for (key, problems) in data {
let filteredProblems = problems.filter { $0.title.localizedCaseInsensitiveContains(searchText) }
if !filteredProblems.isEmpty {
filteredResults[key] = filteredProblems
}
}
return filteredResults
}
}
而核心的 SwiftUI 邏輯十分簡單,利用 ForEach
找出主題顯示在 Section
上,再一次 ForEach
去顯示主題下的題目。
var body: some View {
NavigationStack {
List{
ForEach(searchResults.keys.sorted(), id: \.self){ key in
Section(header: Text(key)) {
ForEach(searchResults[key]!, id: \.self) { item in
NavigationLink(destination: Text(item.title)) {
Text(item.title)
}
}
}
}
}
}
.searchable(text: $searchText)
}
最後成果畫面如下:
成功對應主題顯示我們需要的 LeetCode 題目,且搜尋可以找到我們想要的題目。
圖片範例是透過 Coin
搜尋關鍵字,能夠成功找到 Coin Change
題目。
這系列快完結了,有點不捨,其實 SwiftUI 還有很多值得去探索的地方,這次學到的功能十分實用,在查資料以及攥寫這個 App 時收穫非常多,希望在觀看的你會喜歡本篇介紹。