SwiftUI 中的鍵盤快捷鍵
你有沒有用過 Mac 應用時想過——“要是這個功能能用快捷鍵直接觸發就好了,不用每次都點按鈕”?
SwiftUI 裏面,這個功能超級容易加,用 .keyboardShortcut() 就行。
基本格式是這樣的:
.keyboardShortcut("鍵", modifiers: [.command, .shift])
比如,你想用 Command + C 來觸發一個動作,只要這樣寫:
Button("點擊我") {
print("按鈕被點了!")
}
.keyboardShortcut("C", modifiers: [.command])
按下 Command + C,這個按鈕的代碼就會執行。
痛點分析:
假設你做的是一個 macOS 錄音管理軟件,用户一天可能要導出幾十次音頻文件。
如果每次都得用鼠標點擊“導出”按鈕,效率很低,還容易點錯。
加一個快捷鍵(比如 Command + E 導出),直接秒操作,用户體驗瞬間提升。
額外示例:多個快捷鍵支持同一操作
struct ContentView: View {
var body: some View {
Button("保存文件") {
print("文件已保存")
}
.keyboardShortcut("S", modifiers: [.command])
.keyboardShortcut(.return, modifiers: [.command]) // Command + 回車
}
}
這裏用户既能用 Command + S 保存,也能用 Command + 回車 保存,非常靈活。
控制 SwiftUI 應用的第一個界面
當你的應用啓動時,你通常不想每次都顯示同一個頁面,而是要根據情況來決定顯示什麼。
比如用户已經登錄了,那就直接進主界面;沒登錄,就去登錄頁。
基礎寫法:
@main
struct MyApp: App {
@State private var isLoggedIn = false
var body: some Scene {
WindowGroup {
if isLoggedIn {
MainView()
} else {
LoginView()
}
}
}
}
痛點分析:
想象一下你用銀行 App,剛剛登錄過,五分鐘後回來,結果它又讓你重新輸賬號密碼。
是不是很煩?
通過控制初始界面,你可以讓用户直接回到之前的狀態,減少重複登錄的麻煩。
額外示例:結合 UserDefaults 自動記住登錄狀態
@main
struct MyApp: App {
@State private var isLoggedIn = UserDefaults.standard.bool(forKey: "isLoggedIn")
var body: some Scene {
WindowGroup {
if isLoggedIn {
MainView()
} else {
LoginView(onLoginSuccess: {
UserDefaults.standard.set(true, forKey: "isLoggedIn")
isLoggedIn = true
})
}
}
}
}
這樣只要用户登錄成功一次,下次啓動 App 就會直接進主界面。
NetworkManager & AuthManager:為什麼會報“private 初始化器不可訪問”
你的 NetworkManager 是用 單例模式 寫的:
class NetworkManager {
static let shared = NetworkManager()
private init() {}
}
這樣做的意思是:
“外部禁止用NetworkManager()創建對象,只能用我提供的shared共享實例。”
但是在 AuthManager 裏面,你寫了:
private let networkManager = NetworkManager() // 報錯
這就違反了規則,所以 Swift 提示 “initializer is inaccessible due to 'private' protection level”。
正確做法:
private let networkManager = NetworkManager.shared
這樣你用的就是那個唯一的共享實例。
痛點分析:
如果你不小心創建了多個 NetworkManager 實例,每個實例的 URLSession 和認證狀態可能不一樣,
結果可能出現:
- 登錄成功,但上傳文件時另一個實例不知道你已經登錄。
- API 請求丟失了認證頭,導致無緣無故失敗。
這些都是很隱蔽、讓人抓狂的 Bug。
額外示例:一個帶 Token 的 NetworkManager
class NetworkManager {
static let shared = NetworkManager()
private var authToken: String?
private init() {}
func setToken(_ token: String) {
self.authToken = token
}
func getRequest(url: String) async throws -> Data {
var request = URLRequest(url: URL(string: url)!)
if let token = authToken {
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
let (data, _) = try await URLSession.shared.data(for: request)
return data
}
}
然後在 AuthManager 登錄後:
let response = try await networkManager.login(email: "a@b.com", password: "123456")
NetworkManager.shared.setToken(response.token)
這樣後續所有 API 調用都會自動帶上 Token。