本篇要來關注 LeetCode 題目下面有三個區塊是被收起來,等到用戶去打開它才會展開。
分別是 Discussion
(題目討論)、Similar Questions
(類似的題目)、Related Topics
(相關主題)。
那麼它們到底要怎麼在 SwiftUI 被實踐呢?就是本次要探討的效果了!看完本篇,我們也差不多把 LeetCode 詳細頁的內容補到最完整,這已經是這系列最後五篇了,想想有點捨不得,但感覺還學不夠,意猶未盡。
參考一下 LeetCode 網站,我想要的是如圖效果。
也就是說用戶點擊後,就可以展開內容,再次點擊會把內容收回去不顯示,要在 SwiftUI 製作出折疊效果。
這個效果看起來非常簡單,但是也是花了點時間研究發現 SwiftUI 設定比較特別,就讓我娓娓道來。
首先想要折疊效果,網路上第一個找到的答案會是如下程式碼,當然看到就會很開心套用了。
DisclosureGroup(
isExpanded: $isExpanded,
content: {
Text("這是展開的內容。")
},
label: {
Text("點我展開")
}
)
但是我們實際看畫面會發現,那個箭頭 Icon 不是我們要的方向。
於是就開始搜尋有沒有可能把它改掉的可能性,可是找了許久,結論是沒有地方可以改。看了官方文件,這個元件就是簡單到不行沒地方可以客製化。
所以我們就改自己去拼湊這個 UI 效果,左邊文字右邊 Icon 的組合,就會是使用 HStack
去編排這兩個元件。
HStack{
Text("**Related Topics**")
.foregroundColor(.gray)
.frame(maxWidth: .infinity, alignment: .leading)
Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
.foregroundColor(isExpanded ? .gray : .gray)
.frame(maxWidth: .infinity, alignment: .trailing)
}
需要注意的地方是有設定 .frame(maxWidth: .infinity, alignment: .leading)
表示文字標題是撐滿寬度靠左邊,而圖片 Icon 則是 .frame(maxWidth: .infinity, alignment: .trailing)
撐滿寬度靠右邊。
如果沒設定那麼文字跟圖片會一左一右兩個擠在一起,在找這個效果時也是花了一點時間,因為這跟以前用約束的方式設定差別不小。
再來是製作點擊後會展開的效果,點擊這裡利用 Button
作為點擊監聽事件。
Button(action: {
withAnimation {
isExpanded.toggle()
}
}){
HStack{
Text("**Related Topics**")
.foregroundColor(.gray)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
.foregroundColor(isExpanded ? .gray : .gray)
.frame(maxWidth: .infinity, alignment: .trailing)
.contentShape(Rectangle())
}
}.buttonStyle(.plain)
這邊要注意一個點是我不想讓按鈕點擊有閃爍(Highlight)的效果,但導致點擊範圍只有左邊文字跟右邊 Icon ,可是我想讓那整條包含空白區塊都可以點擊,所以在這兩個元件中分別加上 .contentShape(Rectangle())
告訴 SwiftUI 這是有空間要能夠點擊。
而展開效果的狀態設定為:
@State private var isExpanded = false
所以因為要有展開內容因此使用 VStack
垂直布局包裝起來。當 isExpanded
展開參數為 true 才會長出展開的內容。
VStack(alignment: .leading) {
Button {
// 上述一樣邏輯,這裡避免程式碼太長省略
}
if isExpanded {
Text("這是展開的內容。")
.padding([.top, .bottom], 4)
}
}.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 5))
padding
只有特定邊要設定的話有兩種寫法,一種是用 EdgeInsets
設定,另外一種就是給集合先定義是哪個邊要設定,再給數值。
再來就是加入展開後我們要補上的主題標籤。
if isExpanded {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 16) {
ForEach(0..<10) {
Text("Topic \($0)")
.font(.system(size: 14))
.foregroundStyle(.black)
.padding(8)
.background(Color("ColorGray"))
.cornerRadius(15)
}
}
}
}
ScrollView 設定 .horizontal
表示橫向滾動,為多個標籤時預留空間。
而 showsIndicators: false
則是隱藏滾動條(Scroll Bar),因為在畫面上有點擋路,而且能左右滑動本身在手機端算是直覺。
這裡故意塞十個 Text 標籤為了看滾動效果,特別注意的設定是 .font(.system(size: 14))
可以設置字體大小,.cornerRadius(15)
則是背景灰色後給圓角設定,.padding(8)
則是給他灰圓角底跟文字空間,要注意每個順序都要對,不然效果會出不來,因為程式是一行一行執行的。
最後畫面如下,完美完成 LeetCode 題目描述下面的展開折疊效果。
本次的主題看似很簡單製作,其實身為新手小白的我嘗試改了好幾次程式碼,都沒有想要的效果,所以製作 UI 其實並沒想像中的簡單,還是要對排版、呈現方式運作邏輯要有概念才行,不然開發出來的 App 會覺得功能都有,但是不好用,一個 App 很重要的是『使用者體驗』,這是另外一個要學習的旅程了就不贅述,總之希望大家會喜歡本篇介紹。