iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0

本篇要來關注 LeetCode 題目下面有三個區塊是被收起來,等到用戶去打開它才會展開。

分別是 Discussion (題目討論)、Similar Questions (類似的題目)、Related Topics (相關主題)。

那麼它們到底要怎麼在 SwiftUI 被實踐呢?就是本次要探討的效果了!看完本篇,我們也差不多把 LeetCode 詳細頁的內容補到最完整,這已經是這系列最後五篇了,想想有點捨不得,但感覺還學不夠,意猶未盡。

SwiftUI 折疊效果

參考一下 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 很重要的是『使用者體驗』,這是另外一個要學習的旅程了就不贅述,總之希望大家會喜歡本篇介紹。


上一篇
Day 24: 導讀 LeetCode 演算法 - Graph 的 DFS 與 BFS (Swift)
下一篇
Day 26: SwiftUI 計時器 Timer:計算 LeetCode 刷題時間
系列文
用 SwiftUI 魔法變出 Leetcode 刷題知識學習 App!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言