iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
Mobile Development

用 SwiftUI 魔法變出 Leetcode 刷題知識學習 App!系列 第 26

Day 26: SwiftUI 計時器 Timer:計算 LeetCode 刷題時間

  • 分享至 

  • xImage
  •  

LeetCode 刷題時總是要計算一下自己到底解題解了多久, LeetCode 右上角剛好有一個很佛心的計時器可以幫助我們解決這個問題,而本篇要把這個功能利用 SwiftUI 做進去 App 裏面。

SwiftUI 計時器

為了更完美計算出 LeetCode 寫題的時間,所以需要製作一個計時器計算我們每一題到底花了多少時間,這個計時器不只是定時計算時間,而且每秒需要更新到畫面上。

如圖我們需要一開始顯示時鐘 Icon,點擊後開始從一秒往上疊加自動計時,再點一次計時文字,則會隱藏,再次顯示時鐘 Icon,而時鐘 Icon 會隨著上次有時間顯示成藍色,但是點擊計時文字右邊的 Icon 會重置秒數,導致時鐘 Icon 也會因為重置 0 而顏色變成灰色。

 let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

首先我們製造一個 timer 它會自動計數。

// 記錄計時秒數狀態,隨時更新 UI 
@State var currentTime = 0

Text("\(currentTime)")
    .font(.system(size: 18))
    .padding(8)
    .foregroundStyle(.black)
    .onReceive(timer) { input in
          currentTime += 1
    }

顯示計時文字設定如上,這裡比較特別的是用了 .onReceive(timer) 去監聽 timer 更新後會有通知,而接收了通知後 currentTime 就加一。

但是我們要顯示的格式是 HH:MM:SS 這種小時+分鐘+秒數的格式,所以有個轉換格式的方法。

func formattedTime(_ seconds: Int) -> String {
     let hours = seconds / 3600
     let minutes = (seconds % 3600) / 60
     let seconds = seconds % 60
            
     return String(format: "%02d : %02d : %02d", hours, minutes, seconds)
}

計時文字右邊有個重置 Icon 這裡的設計如下,因為點擊後會把狀態清除,所以利用了 .onTapGesture 製作點擊監聽事件。(因為懶得再外層加上 Button)

Image(systemName: "clock")
     .resizable()
     .scaledToFit()
     .frame(width: 30, height: 30)
     .foregroundColor(.black)
     .onTapGesture {
				// 重置狀態參數
         isShowTimer = false
         currentTime = 0
}

另外可以留意到 systemName: "clock" 這個設定是直接取用 iOS 內建的 Icon 圖,我們就不必另外塞圖到資源檔案。

詳細可以到 Apple 官網查看有哪些 Icon 哦!它叫做『SF Symbols』。

完整程式碼如下,如果想 run 看看可以複製回去使用。

struct ContentView: View {
    @State var currentTime = 0
    @State var isShowTimer = false
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    
    var body: some View {
        
        Button(action: {
            isShowTimer.toggle()
        }) {
            if(isShowTimer){
                let time = formattedTime(currentDate)
                HStack{
                    Text("\(time)")
                        .font(.system(size: 18))
                        .padding(8)
                        .foregroundStyle(.black)
                        .onReceive(timer) { input in
                            currentTime += 1
                        }
                    Image(systemName: "clock")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 30, height: 30)
                        .foregroundColor(.black)
                        .onTapGesture {
                            isShowTimer = false
                            currentTime = 0
                        }
                }.padding(8)
                    .background(
                        Color("ColorGray")
                            .clipShape(RoundedRectangle(cornerRadius:10))
                    )
                
            }else{
                
                Image(systemName: "stopwatch")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 50, height: 50)
                    .foregroundColor((currentTime != 0) ? .blue : .gray)
            }
            
        }
        
        
    }
    
    func formattedTime(_ seconds: Int) -> String {
            let hours = seconds / 3600
            let minutes = (seconds % 3600) / 60
            let seconds = seconds % 60
            
            return String(format: "%02d : %02d : %02d", hours, minutes, seconds)
        }
}

完整畫面如圖,一個完美的計時器就完成了,LeetCode 刷題紀錄時間就靠它。

總結

本篇其實意外的覺得 Timer 與 SwiftUI 的互動特別簡單,以往來說寫法都會特別複雜,但是這次學起來發現居然寫個幾行就結束,覺得不可思議,希望大家喜歡這篇教學。


上一篇
Day 25: SwiftUI 顯示 LeetCode 提示折疊效果
下一篇
Day 27: 導讀 LeetCode 演算法 - 動態規劃 Dynamic Programming (Swift)
系列文
用 SwiftUI 魔法變出 Leetcode 刷題知識學習 App!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言