Files
Bitcoin-Monitoring/test1/BTCMenuBarApp.swift
2025-10-28 21:13:13 +08:00

213 lines
7.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// BTCMenuBarApp.swift
// test1
//
// Created by zl_vm on 2025/10/28.
//
import SwiftUI
import AppKit
import Combine
// macOS
@MainActor
class BTCMenuBarApp: NSObject, ObservableObject {
private var statusItem: NSStatusItem?
private var popover: NSPopover?
private var priceManager = PriceManager()
private var cancellables = Set<AnyCancellable>()
override init() {
super.init()
setupMenuBar()
}
//
private func setupMenuBar() {
//
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
guard let statusItem = statusItem else {
print("❌ 无法创建状态栏项目")
return
}
guard let button = statusItem.button else {
print("❌ 无法获取状态栏按钮")
return
}
// BTC
updateMenuBarTitle(price: 0.0)
button.action = #selector(menuBarClicked)
button.target = self
//
priceManager.$currentPrice
.receive(on: DispatchQueue.main)
.sink { [weak self] price in
self?.updateMenuBarTitle(price: price)
}
.store(in: &cancellables)
}
//
private func updateMenuBarTitle(price: Double) {
DispatchQueue.main.async {
guard let button = self.statusItem?.button else { return }
// BTC
let btcImage = NSImage(systemSymbolName: "bitcoinsign.circle.fill", accessibilityDescription: "BTC")
btcImage?.size = NSSize(width: 16, height: 16)
//
button.image = btcImage
//
if price == 0.0 {
if self.priceManager.isFetching {
button.title = " 更新中..."
} else if self.priceManager.lastError != nil {
button.title = " 错误"
} else {
button.title = " 加载中..."
}
} else {
button.title = String(format: " $%.2f", price)
}
}
}
//
@objc private func menuBarClicked() {
guard let button = statusItem?.button else {
print("❌ 无法获取状态栏按钮")
return
}
showMenu(from: button)
}
//
private func showMenu(from view: NSView) {
let menu = NSMenu()
// BTC
let priceItem = NSMenuItem(title: priceManager.formattedPrice, action: nil, keyEquivalent: "")
if let btcImage = NSImage(systemSymbolName: "bitcoinsign.circle.fill", accessibilityDescription: "BTC") {
btcImage.size = NSSize(width: 16, height: 16)
priceItem.image = btcImage
}
priceItem.isEnabled = false
menu.addItem(priceItem)
//
if let errorMessage = priceManager.errorMessage {
let errorItem = NSMenuItem(title: "错误: \(errorMessage)", action: nil, keyEquivalent: "")
if let errorImage = NSImage(systemSymbolName: "exclamationmark.triangle.fill", accessibilityDescription: "错误") {
errorImage.size = NSSize(width: 16, height: 16)
errorItem.image = errorImage
}
errorItem.isEnabled = false
menu.addItem(errorItem)
menu.addItem(NSMenuItem.separator())
}
//
let timeItem = NSMenuItem(title: "上次更新: \(getCurrentTime())", action: nil, keyEquivalent: "")
if let clockImage = NSImage(systemSymbolName: "clock", accessibilityDescription: "时间") {
clockImage.size = NSSize(width: 16, height: 16)
timeItem.image = clockImage
}
timeItem.isEnabled = false
menu.addItem(timeItem)
menu.addItem(NSMenuItem.separator())
//
let refreshTitle = priceManager.isFetching ? "刷新中..." : "刷新价格"
let refreshItem = NSMenuItem(title: refreshTitle, action: #selector(refreshPrice), keyEquivalent: "r")
if let refreshImage = NSImage(systemSymbolName: priceManager.isFetching ? "hourglass" : "arrow.clockwise", accessibilityDescription: "刷新") {
refreshImage.size = NSSize(width: 16, height: 16)
refreshItem.image = refreshImage
}
refreshItem.target = self
refreshItem.isEnabled = !priceManager.isFetching
menu.addItem(refreshItem)
menu.addItem(NSMenuItem.separator())
//
let aboutItem = NSMenuItem(title: "关于", action: #selector(showAbout), keyEquivalent: "")
if let infoImage = NSImage(systemSymbolName: "info.circle", accessibilityDescription: "关于") {
infoImage.size = NSSize(width: 16, height: 16)
aboutItem.image = infoImage
}
aboutItem.target = self
menu.addItem(aboutItem)
// 退退
let quitItem = NSMenuItem(title: "退出", action: #selector(quitApp), keyEquivalent: "q")
if let quitImage = NSImage(systemSymbolName: "power", accessibilityDescription: "退出") {
quitImage.size = NSSize(width: 16, height: 16)
quitItem.image = quitImage
}
quitItem.target = self
menu.addItem(quitItem)
//
guard let statusItem = statusItem,
let button = statusItem.button else {
print("❌ 无法显示菜单 - 状态栏项目不可用")
return
}
statusItem.menu = menu
button.performClick(nil)
statusItem.menu = nil
}
//
private func getCurrentTime() -> String {
let formatter = DateFormatter()
formatter.timeStyle = .medium
return formatter.string(from: Date())
}
//
@objc private func refreshPrice() {
Task {
await priceManager.refreshPrice()
}
}
//
@objc private func showAbout() {
let alert = NSAlert()
alert.messageText = "BTC价格监控器"
alert.informativeText = """
🚀 一个专业的macOS菜单栏应用用于实时显示BTC价格
✨ 功能特性:
• 实时显示BTC/USDT价格
• 每30秒自动刷新
• 支持手动刷新 (Cmd+R)
• 智能错误重试机制
• 优雅的SF Symbols图标
📊 技术信息:
数据来源币安官方API
作者:张雷
版本1.0.0
架构SwiftUI + AppKit
"""
alert.alertStyle = .informational
alert.addButton(withTitle: "确定")
alert.runModal()
}
// 退
@objc private func quitApp() {
NSApplication.shared.terminate(nil)
}
}